﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Область ПрограммныйИнтерфейс

////////////////////////////////////////////////////////////////////////////////
// Основные процедуры и функции.

// Возвращает текущего пользователя или текущего внешнего пользователя,
// в зависимости от того, кто выполнил вход в сеанс.
//  Рекомендуется использовать в коде, который поддерживает работу в обоих случаях.
//
// Возвращаемое значение:
//  СправочникСсылка.Пользователи, СправочникСсылка.ВнешниеПользователи - пользователь
//    или внешний пользователь.
//
Функция АвторизованныйПользователь() Экспорт
	
	Возврат ПользователиСлужебный.АвторизованныйПользователь();
	
КонецФункции

// Возвращает текущего пользователя.
//  Рекомендуется использовать в коде, который не поддерживает работу с внешними пользователями.
//
//  Если вход в сеанс выполнил внешний пользователь, тогда будет вызвано исключение.
//
// Возвращаемое значение:
//  СправочникСсылка.Пользователи - пользователь.
//
Функция ТекущийПользователь() Экспорт
	
	Возврат ПользователиСлужебныйКлиентСервер.ТекущийПользователь(АвторизованныйПользователь());
	
КонецФункции

// Возвращает Истина, если вход в сеанс выполнил внешний пользователь.
//
// Возвращаемое значение:
//  Булево - Истина, если вход в сеанс выполнил внешний пользователь.
//
Функция ЭтоСеансВнешнегоПользователя() Экспорт
	
	Возврат ПользователиСлужебныйПовтИсп.ЭтоСеансВнешнегоПользователя();
	
КонецФункции

// Проверяет, является ли текущий или указанный пользователь полноправным.
// 
// Полноправным считается пользователь, который:
// а) при непустом списке пользователей информационной базы
//    имеет роль ПолныеПрава и роль для администрирования системы (если ПроверятьПраваАдминистрированияСистемы = Истина);
// б) при пустом списке пользователей информационной базы
//    основная роль конфигурации не задана или ПолныеПрава.
//
// Параметры:
//  Пользователь - Неопределено - проверяется текущий пользователь ИБ.
//               - СправочникСсылка.Пользователи
//               - СправочникСсылка.ВнешниеПользователи - осуществляется
//                    поиск пользователя ИБ по уникальному идентификатору, заданному в реквизите
//                    ИдентификаторПользователяИБ. Если пользователь ИБ не существует, возвращается Ложь.
//               - ПользовательИнформационнойБазы - проверяется указанный пользователь ИБ.
//
//  ПроверятьПраваАдминистрированияСистемы - Булево - если задано Истина, тогда проверяется наличие
//                 роли для администрирования системы.
//
//  УчитыватьПривилегированныйРежим - Булево - если задано Истина, тогда для текущего пользователя
//                 функция возвращает Истина, когда установлен привилегированный режим.
//
// Возвращаемое значение:
//  Булево - если Истина, пользователь является полноправным.
//
Функция ЭтоПолноправныйПользователь(Пользователь = Неопределено,
                                    ПроверятьПраваАдминистрированияСистемы = Ложь,
                                    УчитыватьПривилегированныйРежим = Истина) Экспорт
	
	ПривилегированныйРежимУстановлен = ПривилегированныйРежим();
	
	УстановитьПривилегированныйРежим(Истина);
	СвойстваПользователяИБ = СвойстваПроверяемогоПользователяИБ(Пользователь);
	
	Если СвойстваПользователяИБ = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ПроверитьРольПолныеПрава = Не ПроверятьПраваАдминистрированияСистемы;
	ПроверитьРольАдминистратораСистемы = ПроверятьПраваАдминистрированияСистемы;
	
	Если Не СвойстваПользователяИБ.УказанТекущийПользовательИБ Тогда
		Роли = СвойстваПользователяИБ.ПользовательИБ.Роли;
		
		// Для не текущего пользователя ИБ проверяются роли в записанном пользователе ИБ.
		Если ПроверитьРольПолныеПрава
		   И Не Роли.Содержит(Метаданные.Роли.ПолныеПрава) Тогда
			Возврат Ложь;
		КонецЕсли;
		
		Если ПроверитьРольАдминистратораСистемы
		   И Не Роли.Содержит(Метаданные.Роли.АдминистраторСистемы) Тогда
			Возврат Ложь;
		КонецЕсли;
		
		Возврат Истина;
	КонецЕсли;
	
	Если УчитыватьПривилегированныйРежим И ПривилегированныйРежимУстановлен Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если СтандартныеПодсистемыПовтИсп.ПривилегированныйРежимУстановленПриЗапуске() Тогда
		// Когда клиентское приложение запущено с параметром UsePrivilegedMode, тогда
		// пользователь является полноправным, если привилегированный режим установлен.
		Возврат Истина;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(СвойстваПользователяИБ.Имя) И Метаданные.ОсновныеРоли.Количество() = 0 Тогда
		// Когда основные роли не указаны, тогда у неуказанного пользователя
		// есть все права (как в привилегированном режиме).
		Возврат Истина;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(СвойстваПользователяИБ.Имя)
	   И ПривилегированныйРежимУстановлен
	   И СвойстваПользователяИБ.ПравоАдминистрирование Тогда
		// Когда у неуказанного пользователя есть право Администрирование,
		// тогда привилегированный режим учитывается всегда для поддержки
		// параметра запуска UsePrivilegedMode у не клиентских приложений.
		Возврат Истина;
	КонецЕсли;
	
	// Для текущего пользователя ИБ проверяются роли не в записанном пользователе ИБ,
	// а роли в текущем сеансе.
	Если ПроверитьРольПолныеПрава
	   И Не СвойстваПользователяИБ.РольДоступнаПолныеПрава Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ПроверитьРольАдминистратораСистемы
	   И Не СвойстваПользователяИБ.РольДоступнаАдминистраторСистемы Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

// Возвращает доступность хотя бы одной из указанных ролей или полноправность
// пользователя (текущего или указанного).
//
// Параметры:
//  ИменаРолей   - Строка - имена ролей, разделенные запятыми, доступность которых проверяется.
//
//  Пользователь - Неопределено - проверяется текущий пользователь ИБ.
//               - СправочникСсылка.Пользователи
//               - СправочникСсылка.ВнешниеПользователи - осуществляется
//                    поиск пользователя ИБ по уникальному идентификатору, заданному в реквизите.
//                    ИдентификаторПользователяИБ. Если пользователь ИБ не существует, возвращается Ложь.
//               - ПользовательИнформационнойБазы - проверяется указанный пользователь ИБ.
//
//  УчитыватьПривилегированныйРежим - Булево - если задано Истина, тогда для текущего пользователя
//                 функция возвращает Истина, когда установлен привилегированный режим.
//
// Возвращаемое значение:
//  Булево - Истина, если хотя бы одна из указанных ролей доступна
//           или функция ЭтоПолноправныйПользователь(Пользователь) возвращает Истина.
//
Функция РолиДоступны(ИменаРолей,
                     Пользователь = Неопределено,
                     УчитыватьПривилегированныйРежим = Истина) Экспорт
	
	РольАдминистраторСистемы = ЭтоПолноправныйПользователь(Пользователь, Истина, УчитыватьПривилегированныйРежим);
	РольПолныеПрава          = ЭтоПолноправныйПользователь(Пользователь, Ложь,   УчитыватьПривилегированныйРежим);
	
	Если РольАдминистраторСистемы И РольПолныеПрава Тогда
		Возврат Истина;
	КонецЕсли;
	
	МассивИменРолей = СтрРазделить(ИменаРолей, ",", Ложь);
	
	ТребуетсяРольАдминистраторСистемы = Ложь;
	НазначениеРолей = ПользователиСлужебныйПовтИсп.НазначениеРолей();
	
	Для Каждого ИмяРоли Из МассивИменРолей Цикл
		Если НазначениеРолей.ТолькоДляАдминистраторовСистемы.Получить(ИмяРоли) <> Неопределено Тогда
			ТребуетсяРольАдминистраторСистемы = Истина;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Если РольАдминистраторСистемы И    ТребуетсяРольАдминистраторСистемы
	 Или РольПолныеПрава          И Не ТребуетсяРольАдминистраторСистемы Тогда
		Возврат Истина;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	СвойстваПользователяИБ = СвойстваПроверяемогоПользователяИБ(Пользователь);
	
	Если СвойстваПользователяИБ = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если СвойстваПользователяИБ.УказанТекущийПользовательИБ Тогда
		Для Каждого ИмяРоли Из МассивИменРолей Цикл
			// АПК:336-выкл Не заменять на РолиДоступны. Проверка ролей в функции РолиДоступны.
			//@skip-check using-isinrole
			Если РольДоступна(СокрЛП(ИмяРоли)) Тогда
				Возврат Истина;
			КонецЕсли;
			// АПК:336-вкл
		КонецЦикла;
	Иначе
		Роли = СвойстваПользователяИБ.ПользовательИБ.Роли;
		Для Каждого ИмяРоли Из МассивИменРолей Цикл
			Если Роли.Содержит(Метаданные.Роли.Найти(СокрЛП(ИмяРоли))) Тогда
				Возврат Истина;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

// Проверяет наличие хотя бы одного вида аутентификации у пользователя ИБ.
// Поддерживается вызов из сеанса без установленных разделителей (без обращения к базе данных),
// если параметр ОписаниеПользователяИБ имеет тип ПользовательИнформационнойБазы или Структура.
//
// Параметры:
//  ОписаниеПользователяИБ - УникальныйИдентификатор - идентификатор пользователя ИБ.
//                         - Структура - содержит свойства аутентификации:
//                             * АутентификацияСтандартная    - Булево - аутентификация 1С:Предприятия.
//                             * АутентификацияОС             - Булево - аутентификация операционной системы.
//                             * АутентификацияOpenID         - Булево - аутентификация OpenID.
//                             * АутентификацияOpenIDConnect  - Булево - аутентификация OpenID-Connect.
//                             * АутентификацияТокеномДоступа - Булево - аутентификация JWT-токеном.
//                         - ПользовательИнформационнойБазы       - пользователь ИБ.
//                         - СправочникСсылка.Пользователи        - пользователь.
//                         - СправочникСсылка.ВнешниеПользователи - внешний пользователь.
//
// Возвращаемое значение:
//  Булево - Истина, если хотя бы одно свойство аутентификации равно Истина.
//
Функция ВходВПрограммуРазрешен(ОписаниеПользователяИБ) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	УникальныйИдентификатор = Неопределено;
	
	Если ТипЗнч(ОписаниеПользователяИБ) = Тип("СправочникСсылка.Пользователи")
	 Или ТипЗнч(ОписаниеПользователяИБ) = Тип("СправочникСсылка.ВнешниеПользователи") Тогда
		
		УникальныйИдентификатор = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(
			ОписаниеПользователяИБ, "ИдентификаторПользователяИБ");
		
		Если ТипЗнч(УникальныйИдентификатор) <> Тип("УникальныйИдентификатор") Тогда
			Возврат Ложь;
		КонецЕсли;
		
	ИначеЕсли ТипЗнч(ОписаниеПользователяИБ) = Тип("УникальныйИдентификатор") Тогда
		УникальныйИдентификатор = ОписаниеПользователяИБ;
	КонецЕсли;
	
	Если УникальныйИдентификатор <> Неопределено Тогда
		ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(УникальныйИдентификатор);
		
		Если ПользовательИБ = Неопределено Тогда
			Возврат Ложь;
		КонецЕсли;
	Иначе
		ПользовательИБ = ОписаниеПользователяИБ;
	КонецЕсли;
	
	Возврат ПользовательИБ.АутентификацияСтандартная
		Или ПользовательИБ.АутентификацияOpenID
		Или ПользовательИБ.АутентификацияOpenIDConnect
		Или ПользовательИБ.АутентификацияТокеномДоступа
		Или ПользовательИБ.АутентификацияОС;
	
КонецФункции

// Проверяет, что у пользователя ИБ есть права для входа в программу интерактивно или через COM-объект.
// Поддерживается вызов из сеанса без установленных разделителей (без обращения к базе данных).
//
// Параметры:
//  ПользовательИБ      - ПользовательИнформационнойБазы
//  Интерактивно        - Булево - когда Истина, тогда не учитываются права,
//                          например, ВнешнееСоединение, Automation.
//  ТолькоПраваНаЗапуск - Булево - если Истина, тогда проверяется только наличие прав на запуск,
//                          если Ложь, тогда проверяться еще и наличие минимальных прав для входа.
//
// Возвращаемое значение:
//  Булево
//
Функция ЕстьПраваДляВходаВПрограмму(ПользовательИБ, Интерактивно = Истина, ТолькоПраваНаЗапуск = Истина) Экспорт
	
	Результат =
		    ПравоДоступа("ТонкийКлиент",    Метаданные, ПользовательИБ)
		Или ПравоДоступа("ВебКлиент",       Метаданные, ПользовательИБ)
		Или ПравоДоступа("МобильныйКлиент", Метаданные, ПользовательИБ)
		Или ПравоДоступа("ТолстыйКлиент",   Метаданные, ПользовательИБ);
	
	Если Не Интерактивно Тогда
		Результат = Результат
			Или ПравоДоступа("Automation",        Метаданные, ПользовательИБ)
			Или ПравоДоступа("ВнешнееСоединение", Метаданные, ПользовательИБ);
	КонецЕсли;
	
	Если Не ТолькоПраваНаЗапуск Тогда
		// АПК:515-выкл - №737.4 проверка роли, так как это признак минимальных прав для входа.
		Результат = Результат И РолиДоступны("БазовыеПраваБСП,
			|БазовыеПраваВнешнихПользователейБСП", ПользовательИБ, Ложь);
		// АПК:515-вкл
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Следует вызывать в начале процедур http-сервисов, веб-сервисов, com-соединений,
// если они используются для удаленного подключения обычных пользователей,
// чтобы обеспечить контроль ограничений на вход (по дате, по активности и т.д.),
// обновить дату последнего входа, а также сразу заполнить параметры сеанса
// АвторизованныйПользователь, ТекущийПользователь, ТекущийВнешнийПользователь.
//
// Процедура вызывается автоматически только при интерактивном входе,
// то есть когда ТекущийРежимЗапуска() <> Неопределено.
//
// Параметры:
//  ВызыватьИсключение - Булево - вызывать исключение в случае ошибки авторизации,
//                                в противном случае возвращать текст ошибки.
// Возвращаемое значение:
//  Структура:
//   * ОшибкаАвторизации      - Строка - текст ошибки, если заполнено.
//   * ТребуетсяСменитьПароль - Булево - если Истина, тогда это ошибка устаревания пароля.
//
Функция АвторизоватьТекущегоПользователяПриВходе(ВызыватьИсключение = Истина) Экспорт
	
	Результат = ПользователиСлужебный.АвторизоватьТекущегоПользователяПриВходе(Истина);
	
	Если ВызыватьИсключение И ЗначениеЗаполнено(Результат.ОшибкаАвторизации) Тогда
		ВызватьИсключение Результат.ОшибкаАвторизации;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Возвращает Истина, если определяемый тип ФизическоеЛицо содержит
// тип отличный от Строка и в процедуре ПриОпределенииНастроек
// общего модуля ПользователиПереопределяемый настроено
// использовать реквизит ФизическоеЛицо.
//
// Возвращаемое значение:
//  Булево
//
Функция ФизическоеЛицоИспользуется() Экспорт
	
	Возврат ПользователиСлужебныйПовтИсп.Настройки().ФизическоеЛицоИспользуется;
	
КонецФункции

// Возвращает Истина, если определяемый тип Подразделение содержит
// тип отличный от Строка и в процедуре ПриОпределенииНастроек
// общего модуля ПользователиПереопределяемый настроено
// использовать реквизит Подразделение.
//
// Возвращаемое значение:
//  Булево
//
Функция ПодразделениеИспользуется() Экспорт
	
	Возврат ПользователиСлужебныйПовтИсп.Настройки().ПодразделениеИспользуется;
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции, используемые в работе элементов управляемых форм.

// Возвращает список пользователей, групп пользователей, внешних пользователей и
// групп внешних пользователей.
// Для использования в обработчиках событий ОкончаниеВводаТекста и АвтоПодбор.
//
// Параметры:
//  Текст         - Строка - символы, введенные пользователем.
//
//  ВключаяГруппы - Булево - если Истина, включать группы пользователей и внешних пользователей.
//                  Если ФО ИспользоватьГруппыПользователей отключена, параметр игнорируется.
//
//  ВключаяВнешнихПользователей - Неопределено
//                              - Булево - когда Неопределено, используется значение,
//                  возвращаемое функцией ВнешниеПользователи.ИспользоватьВнешнихПользователей.
//
//  БезПользователей - Булево - если Истина, то элементы справочника Пользователи
//                  исключаются из результата.
//
// Возвращаемое значение:
//  СписокЗначений
//
Функция СформироватьДанныеВыбораПользователя(Знач Текст,
                                             Знач ВключаяГруппы = Истина,
                                             Знач ВключаяВнешнихПользователей = Неопределено,
                                             Знач БезПользователей = Ложь) Экспорт
	
	ВключаяГруппы = ВключаяГруппы И ПолучитьФункциональнуюОпцию("ИспользоватьГруппыПользователей");
	
	Запрос = Новый Запрос(
		"ВЫБРАТЬ
		|	ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка) КАК Ссылка,
		|	"""" КАК Наименование,
		|	-1 КАК НомерКартинки
		|ГДЕ
		|	ЛОЖЬ");
	
	Если Не БезПользователей
	   И ПравоДоступа("Чтение", Метаданные.Справочники.Пользователи)Тогда
		
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	Пользователи.Ссылка КАК Ссылка,
		|	Пользователи.Наименование КАК Наименование,
		|	ЕСТЬNULL(СведенияОПользователях.НомерКартинкиСостояния, 0) - 1 КАК НомерКартинки
		|ИЗ
		|	Справочник.Пользователи КАК Пользователи
		|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СведенияОПользователях КАК СведенияОПользователях
		|		ПО СведенияОПользователях.Пользователь = Пользователи.Ссылка
		|ГДЕ
		|	Пользователи.Наименование ПОДОБНО &Текст СПЕЦСИМВОЛ ""~""
		|	И Пользователи.Недействителен = ЛОЖЬ
		|	И Пользователи.Служебный = ЛОЖЬ
		|
		|ОБЪЕДИНИТЬ ВСЕ
		|
		|ВЫБРАТЬ
		|	ГруппыПользователей.Ссылка,
		|	ГруппыПользователей.Наименование,
		|	ВЫБОР
		|		КОГДА ГруппыПользователей.ПометкаУдаления
		|			ТОГДА 2
		|		ИНАЧЕ 3
		|	КОНЕЦ
		|ИЗ
		|	Справочник.ГруппыПользователей КАК ГруппыПользователей
		|ГДЕ
		|	&ВключаяГруппы
		|	И ГруппыПользователей.Наименование ПОДОБНО &Текст СПЕЦСИМВОЛ ""~""";
		
		Запрос.Текст = Запрос.Текст + " ОБЪЕДИНИТЬ ВСЕ " + ТекстЗапроса;
	КонецЕсли;
	
	Запрос.УстановитьПараметр("Текст", ОбщегоНазначения.СформироватьСтрокуДляПоискаВЗапросе(Текст) + "%");
	Запрос.УстановитьПараметр("ВключаяГруппы", ВключаяГруппы);

	Если ТипЗнч(ВключаяВнешнихПользователей) <> Тип("Булево") Тогда
		ВключаяВнешнихПользователей = ВнешниеПользователи.ИспользоватьВнешнихПользователей();
	КонецЕсли;
	ВключаяВнешнихПользователей = ВключаяВнешнихПользователей
		И ПравоДоступа("Чтение", Метаданные.Справочники.ВнешниеПользователи);
	
	Если ВключаяВнешнихПользователей Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	ВнешниеПользователи.Ссылка КАК Ссылка,
		|	ВнешниеПользователи.Наименование КАК Наименование,
		|	ЕСТЬNULL(СведенияОПользователях.НомерКартинкиСостояния, 0) - 1 КАК НомерКартинки
		|ИЗ
		|	Справочник.ВнешниеПользователи КАК ВнешниеПользователи
		|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СведенияОПользователях КАК СведенияОПользователях
		|		ПО СведенияОПользователях.Пользователь = ВнешниеПользователи.Ссылка
		|ГДЕ
		|	ВнешниеПользователи.Наименование ПОДОБНО &Текст СПЕЦСИМВОЛ ""~""
		|	И ВнешниеПользователи.Недействителен = ЛОЖЬ
		|
		|ОБЪЕДИНИТЬ ВСЕ
		|
		|ВЫБРАТЬ
		|	ГруппыВнешнихПользователей.Ссылка,
		|	ГруппыВнешнихПользователей.Наименование,
		|	ВЫБОР
		|		КОГДА ГруппыВнешнихПользователей.ПометкаУдаления
		|			ТОГДА 8
		|		ИНАЧЕ 9
		|	КОНЕЦ
		|ИЗ
		|	Справочник.ГруппыВнешнихПользователей КАК ГруппыВнешнихПользователей
		|ГДЕ
		|	&ВключаяГруппы
		|	И ГруппыВнешнихПользователей.Наименование ПОДОБНО &Текст СПЕЦСИМВОЛ ""~""";
		
		Запрос.Текст = Запрос.Текст + " ОБЪЕДИНИТЬ ВСЕ " + ТекстЗапроса;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	Выборка = Запрос.Выполнить().Выбрать();
	УстановитьПривилегированныйРежим(Ложь);
	
	ДанныеВыбора = Новый СписокЗначений;
	
	Пока Выборка.Следующий() Цикл
		ДанныеВыбора.Добавить(Выборка.Ссылка, Выборка.Наименование, ,
			БиблиотекаКартинок["СостояниеПользователя" + Формат(Выборка.НомерКартинки + 1, "ЧЦ=2; ЧВН=; ЧГ=")]);
	КонецЦикла;
	
	Возврат ДанныеВыбора;
	
КонецФункции

// Заполняет номера картинок пользователей, групп пользователей, внешних пользователей и групп внешних пользователей
// во всех строках или указанной (см. параметр ИдентификаторСтроки) строке коллекции ТаблицаИлиДерево.
// 
// Параметры:
//  ТаблицаИлиДерево      - ДанныеФормыКоллекция
//                        - ДанныеФормыДерево - коллекция для заполнения.
//  ИмяПоляПользователь   - Строка - имя колонки в коллекции ТаблицаИлиДерево, содержащей ссылку на пользователя, 
//                                   группу пользователей, внешнего пользователя или группу внешних пользователей.
//                                   По ее значению вычисляется номер картинки.
//  ИмяПоляНомераКартинки - Строка - имя колонки в коллекции ТаблицаИлиДерево с номером картинки, 
//                                   которое требуется заполнить.
//  ИдентификаторСтроки  - Неопределено
//                       - Число - идентификатор строки (не порядковый номер), которую требуется 
//                                 заполнить (у дерева также будут заполнены и дочерние строки),
//                                 если Неопределено, то картинки будут заполнены во всех строках.
//  ОбработатьИерархиюВторогоИТретьегоУровней - Булево - если Истина и в параметре ТаблицаИлиДерево указана 
//                                 коллекция типа ДанныеФормыДерево, то 
//                                 будут заполнены поля по четвертый уровень дерева включительно,
//                                 иначе будут заполнены поля только на первом и втором уровне дерева.
//
Процедура ЗаполнитьНомераКартинокПользователей(Знач ТаблицаИлиДерево,
                                               Знач ИмяПоляПользователь,
                                               Знач ИмяПоляНомераКартинки,
                                               Знач ИдентификаторСтроки = Неопределено,
                                               Знач ОбработатьИерархиюВторогоИТретьегоУровней = Ложь) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Если ИдентификаторСтроки = Неопределено Тогда
		МассивСтрок = Неопределено;
		
	ИначеЕсли ТипЗнч(ИдентификаторСтроки) = Тип("Массив") Тогда
		МассивСтрок = Новый Массив;
		Для каждого Идентификатор Из ИдентификаторСтроки Цикл
			МассивСтрок.Добавить(ТаблицаИлиДерево.НайтиПоИдентификатору(Идентификатор));
		КонецЦикла;
	Иначе
		МассивСтрок = Новый Массив;
		МассивСтрок.Добавить(ТаблицаИлиДерево.НайтиПоИдентификатору(ИдентификаторСтроки));
	КонецЕсли;
	
	Если ТипЗнч(ТаблицаИлиДерево) = Тип("ДанныеФормыДерево") Тогда
		Если МассивСтрок = Неопределено Тогда
			МассивСтрок = ТаблицаИлиДерево.ПолучитьЭлементы();
		КонецЕсли;
		ТаблицаПользователей = Новый ТаблицаЗначений;
		ТаблицаПользователей.Колонки.Добавить(ИмяПоляПользователь,
			Метаданные.РегистрыСведений.СоставыГруппПользователей.Измерения.ГруппаПользователей.Тип);
		Для Каждого Строка Из МассивСтрок Цикл
			ТаблицаПользователей.Добавить()[ИмяПоляПользователь] = Строка[ИмяПоляПользователь];
			Если ОбработатьИерархиюВторогоИТретьегоУровней Тогда
				Для каждого Строка2 Из Строка.ПолучитьЭлементы() Цикл
					ТаблицаПользователей.Добавить()[ИмяПоляПользователь] = Строка2[ИмяПоляПользователь];
					Для каждого Строка3 Из Строка2.ПолучитьЭлементы() Цикл
						ТаблицаПользователей.Добавить()[ИмяПоляПользователь] = Строка3[ИмяПоляПользователь];
					КонецЦикла;
				КонецЦикла;
			КонецЕсли;
		КонецЦикла;
	ИначеЕсли ТипЗнч(ТаблицаИлиДерево) = Тип("ДанныеФормыКоллекция") Тогда
		Если МассивСтрок = Неопределено Тогда
			МассивСтрок = ТаблицаИлиДерево;
		КонецЕсли;
		ТаблицаПользователей = Новый ТаблицаЗначений;
		ТаблицаПользователей.Колонки.Добавить(ИмяПоляПользователь,
			Метаданные.РегистрыСведений.СоставыГруппПользователей.Измерения.ГруппаПользователей.Тип);
		Для Каждого Строка Из МассивСтрок Цикл
			ТаблицаПользователей.Добавить()[ИмяПоляПользователь] = Строка[ИмяПоляПользователь];
		КонецЦикла;
	ИначеЕсли ТипЗнч(ТаблицаИлиДерево) = Тип("Массив") Тогда
		МассивСтрок = ТаблицаИлиДерево;
		ТаблицаПользователей = Новый ТаблицаЗначений;
		ТаблицаПользователей.Колонки.Добавить(ИмяПоляПользователь,
			Метаданные.РегистрыСведений.СоставыГруппПользователей.Измерения.ГруппаПользователей.Тип);
		Для Каждого Строка Из ТаблицаИлиДерево Цикл
			ТаблицаПользователей.Добавить()[ИмяПоляПользователь] = Строка[ИмяПоляПользователь];
		КонецЦикла;
	Иначе
		Если МассивСтрок = Неопределено Тогда
			МассивСтрок = ТаблицаИлиДерево;
		КонецЕсли;
		ТаблицаПользователей = ТаблицаИлиДерево.Выгрузить(МассивСтрок, ИмяПоляПользователь);
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	Пользователи.ИмяПоляПользователь КАК Пользователь
	|ПОМЕСТИТЬ Пользователи
	|ИЗ
	|	&Пользователи КАК Пользователи
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	Пользователи.Пользователь КАК Пользователь,
	|	ВЫБОР
	|		КОГДА Пользователи.Пользователь = НЕОПРЕДЕЛЕНО
	|			ТОГДА -1
	|		КОГДА ТИПЗНАЧЕНИЯ(Пользователи.Пользователь) = ТИП(Справочник.Пользователи)
	|			ТОГДА ЕСТЬNULL(СведенияОПользователях.НомерКартинкиСостояния, 0) - 1
	|		КОГДА ТИПЗНАЧЕНИЯ(Пользователи.Пользователь) = ТИП(Справочник.ГруппыПользователей)
	|			ТОГДА ВЫБОР
	|					КОГДА ВЫРАЗИТЬ(Пользователи.Пользователь КАК Справочник.ГруппыПользователей).ПометкаУдаления
	|						ТОГДА 2
	|					ИНАЧЕ 3
	|				КОНЕЦ
	|		КОГДА ТИПЗНАЧЕНИЯ(Пользователи.Пользователь) = ТИП(Справочник.ВнешниеПользователи)
	|			ТОГДА ЕСТЬNULL(СведенияОПользователях.НомерКартинкиСостояния, 0) - 1
	|		КОГДА ТИПЗНАЧЕНИЯ(Пользователи.Пользователь) = ТИП(Справочник.ГруппыВнешнихПользователей)
	|			ТОГДА ВЫБОР
	|					КОГДА ВЫРАЗИТЬ(Пользователи.Пользователь КАК Справочник.ГруппыВнешнихПользователей).ПометкаУдаления
	|						ТОГДА 8
	|					ИНАЧЕ 9
	|				КОНЕЦ
	|		ИНАЧЕ -2
	|	КОНЕЦ КАК НомерКартинки
	|ИЗ
	|	Пользователи КАК Пользователи
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СведенияОПользователях КАК СведенияОПользователях
	|		ПО СведенияОПользователях.Пользователь = Пользователи.Пользователь";
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "ИмяПоляПользователь", ИмяПоляПользователь);
	Запрос.УстановитьПараметр("Пользователи", ТаблицаПользователей);
	НомераКартинок = Запрос.Выполнить().Выгрузить();
	
	Для Каждого Строка Из МассивСтрок Цикл
		НайденнаяСтрока = НомераКартинок.Найти(Строка[ИмяПоляПользователь], "Пользователь");
		Строка[ИмяПоляНомераКартинки] = ?(НайденнаяСтрока = Неопределено, -2, НайденнаяСтрока.НомерКартинки);
		Если ОбработатьИерархиюВторогоИТретьегоУровней Тогда
			Для каждого Строка2 Из Строка.ПолучитьЭлементы() Цикл
				НайденнаяСтрока = НомераКартинок.Найти(Строка2[ИмяПоляПользователь], "Пользователь");
				Строка2[ИмяПоляНомераКартинки] = ?(НайденнаяСтрока = Неопределено, -2, НайденнаяСтрока.НомерКартинки);
				Для каждого Строка3 Из Строка2.ПолучитьЭлементы() Цикл
					НайденнаяСтрока = НомераКартинок.Найти(Строка3[ИмяПоляПользователь], "Пользователь");
					Строка3[ИмяПоляНомераКартинки] = ?(НайденнаяСтрока = Неопределено, -2, НайденнаяСтрока.НомерКартинки);
				КонецЦикла;
			КонецЦикла;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции, используемые при обновлении информационной базы.

// Используется при обновлении и начальном заполнении информационной базы.
// 1) Создает первого администратора и сопоставляет его с новым или существующим
//    пользователем в справочнике Пользователи.
// 2) Сопоставляет администратора, указанного в параметре ПользовательИБ, с новым или
//    существующим пользователем в справочнике Пользователи.
//
// Параметры:
//  ПользовательИБ - Неопределено - создать первого администратора, если не существует.
//                 - ПользовательИнформационнойБазы - используется, когда нужно сопоставить
//                   существующего администратора с новым или существующим пользователем
//                   в справочнике Пользователи.
//
// Возвращаемое значение:
//  Неопределено                  - первый администратор уже существует.
//  СправочникСсылка.Пользователи - пользователь в справочнике, с которым сопоставлен созданный
//                                  первый администратор или указанный существующий.
//
Функция СоздатьАдминистратора(ПользовательИБ = Неопределено) Экспорт
	
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		ТекстОшибки = НСтр("ru = 'Справочник Пользователи недоступен в неразделенном режиме.'");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	// Добавление администратора.
	Если ПользовательИБ = Неопределено Тогда
		ПользователиИБ = ПользователиИнформационнойБазы.ПолучитьПользователей();
		
		Если ПользователиИБ.Количество() = 0 Тогда
			Если ОбщегоНазначения.РазделениеВключено() Тогда
				ТекстОшибки =
					НСтр("ru = 'Невозможно автоматически создать первого администратора области данных.'");
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
			ПользовательИБ = ПользователиИнформационнойБазы.СоздатьПользователя();
			ПользовательИБ.Имя       = "Администратор";
			ПользовательИБ.ПолноеИмя = ПользовательИБ.Имя;
			ПользовательИБ.Роли.Очистить();
			ПользовательИБ.Роли.Добавить(Метаданные.Роли.ПолныеПрава);
			РольАдминистратораСистемы = Метаданные.Роли.АдминистраторСистемы;
			Если НЕ ПользовательИБ.Роли.Содержит(РольАдминистратораСистемы) Тогда
				ПользовательИБ.Роли.Добавить(РольАдминистратораСистемы);
			КонецЕсли;
			ПользовательИБ.Записать();
		Иначе
			// Если существует пользователь с правами администратора,
			// тогда первый администратор уже создан и его не требуется создавать.
			Для Каждого ТекущийПользовательИБ Из ПользователиИБ Цикл
				Если ПользователиСлужебный.РолиАдминистратораДоступны(ТекущийПользовательИБ) Тогда
					Возврат Неопределено; // Первый администратор уже создан.
				КонецЕсли;
			КонецЦикла;
			// Первый администратор создан некорректно.
			ТекстОшибки =
				НСтр("ru = 'Список пользователей информационной базы не пустой, однако не удалось
				           |найти ни одного пользователя с ролями Полные права и Администратор системы.
				           |
				           |Вероятно, пользователи создавались в конфигураторе.
				           |Назначьте роли Полные права и Администратор системы хотя бы одному пользователю.'");
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
	Иначе
		Если Не ПользователиСлужебный.РолиАдминистратораДоступны(ПользовательИБ) Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Невозможно создать пользователя в справочнике для пользователя
				           |информационной базы ""%1"",
				           |так как у него нет ролей Полные права и Администратор системы.
				           |
				           |Вероятно, пользователь был создан в конфигураторе.
				           |Для автоматического создания пользователя в справочнике требуется
				           |назначить ему роли Полные права и Администратор системы.'"),
				Строка(ПользовательИБ));
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		
		НайтиНеоднозначныхПользователейИБ(Неопределено, ПользовательИБ.УникальныйИдентификатор);
	КонецЕсли;
	
	НачатьТранзакцию();
	Попытка
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить("Справочник.Пользователи");
		ЭлементБлокировки.УстановитьЗначение("ИдентификаторПользователяИБ", ПользовательИБ.УникальныйИдентификатор);
		ЭлементБлокировки = Блокировка.Добавить("Справочник.ВнешниеПользователи");
		ЭлементБлокировки.УстановитьЗначение("ИдентификаторПользователяИБ", ПользовательИБ.УникальныйИдентификатор);
		ЭлементБлокировки = Блокировка.Добавить("Справочник.Пользователи");
		ЭлементБлокировки.УстановитьЗначение("Наименование", ПользовательИБ.ПолноеИмя);
		Блокировка.Заблокировать();
		
		Пользователь = Неопределено;
		ПользователиСлужебный.ПользовательПоИдентификаторуСуществует(ПользовательИБ.УникальныйИдентификатор,, Пользователь);
		Если ТипЗнч(Пользователь) = Тип("СправочникСсылка.ВнешниеПользователи") Тогда
			ВнешнийПользовательОбъект = Пользователь.ПолучитьОбъект();
			ВнешнийПользовательОбъект.ИдентификаторПользователяИБ = Неопределено;
			ОбновлениеИнформационнойБазы.ЗаписатьДанные(ВнешнийПользовательОбъект);
			Пользователь = Неопределено;
		КонецЕсли;

		Если НЕ ЗначениеЗаполнено(Пользователь) Тогда
			Пользователь = Справочники.Пользователи.НайтиПоНаименованию(ПользовательИБ.ПолноеИмя);
			
			Если ЗначениеЗаполнено(Пользователь)
			   И ЗначениеЗаполнено(Пользователь.ИдентификаторПользователяИБ)
			   И Пользователь.ИдентификаторПользователяИБ <> ПользовательИБ.УникальныйИдентификатор
			   И ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(
			         Пользователь.ИдентификаторПользователяИБ) <> Неопределено Тогда
				
				Пользователь = Неопределено;
			КонецЕсли;
		КонецЕсли;
		
		Если НЕ ЗначениеЗаполнено(Пользователь) Тогда
			Пользователь = Справочники.Пользователи.СоздатьЭлемент();
			ПользовательСоздан = Истина;
		Иначе
			Пользователь = Пользователь.ПолучитьОбъект();
			ПользовательСоздан = Ложь;
		КонецЕсли;
		
		Пользователь.Наименование = ПользовательИБ.ПолноеИмя;
		
		ОписаниеПользователяИБ = Новый Структура;
		ОписаниеПользователяИБ.Вставить("Действие", "Записать");
		ОписаниеПользователяИБ.Вставить("УникальныйИдентификатор", ПользовательИБ.УникальныйИдентификатор);
		Пользователь.ДополнительныеСвойства.Вставить(
			"ОписаниеПользователяИБ", ОписаниеПользователяИБ);
		Пользователь.ДополнительныеСвойства.Вставить("СозданиеАдминистратора",
			?(ПользовательИБ = Неопределено,
			  НСтр("ru = 'Выполнено создание первого администратора.'"),
			  ?(ПользовательСоздан,
			    НСтр("ru = 'Администратор сопоставлен с новым пользователем справочника.'"),
			    НСтр("ru = 'Администратор сопоставлен с существующим пользователем справочника.'")) ) );
			
		Пользователь.Записать();
	
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	Возврат Пользователь.Ссылка;
	
КонецФункции

// Устанавливает константу ИспользоватьГруппыПользователей в Истина,
// если существует хотя бы одна группа пользователей в справочнике.
//
// Используется при обновлении информационной базы.
//
Процедура ПриНаличииГруппПользователейУстановитьИспользование() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ
	|	ИСТИНА КАК ЗначениеИстина
	|ИЗ
	|	Справочник.ГруппыПользователей КАК ГруппыПользователей
	|ГДЕ
	|	ГруппыПользователей.Ссылка <> ЗНАЧЕНИЕ(Справочник.ГруппыПользователей.ВсеПользователи)
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ
	|	ИСТИНА
	|ИЗ
	|	Справочник.ГруппыВнешнихПользователей КАК ГруппыВнешнихПользователей
	|ГДЕ
	|	ГруппыВнешнихПользователей.Ссылка <> ЗНАЧЕНИЕ(Справочник.ГруппыВнешнихПользователей.ВсеВнешниеПользователи)");
	
	Если НЕ Запрос.Выполнить().Пустой() Тогда
		Константы.ИспользоватьГруппыПользователей.Установить(Истина);
	КонецЕсли;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции для работы с пользователями информационной базы.

// Возвращает текстовое представление "<Не указан>" для случаев, когда пользователь не указан или не выбран.
// См. также Пользователи.СсылкаНеуказанногоПользователя.
//
// Возвращаемое значение:
//   Строка
//
Функция ПолноеИмяНеуказанногоПользователя() Экспорт
	
	Возврат "<" + НСтр("ru = 'Не указан'") + ">";
	
КонецФункции

// Возвращает ссылку неуказанного пользователя.
// См. также Пользователи.ПолноеИмяНеуказанногоПользователя.
//
// Параметры:
//  СоздатьЕслиНеСуществует - Булево - если Истина, то будет создан пользователь "<Не указан>".
//
// Возвращаемое значение:
//  СправочникСсылка.Пользователи
//  Неопределено - если неуказанный пользователь не существует в справочнике.
//
Функция СсылкаНеуказанногоПользователя(СоздатьЕслиНеСуществует = Ложь) Экспорт
	
	Ссылка = ПользователиСлужебный.СвойстваНеуказанногоПользователя().Ссылка;
	
	Если Ссылка = Неопределено И СоздатьЕслиНеСуществует Тогда
		Ссылка = ПользователиСлужебный.СоздатьНеуказанногоПользователя();
	КонецЕсли;
	
	Возврат Ссылка;
	
КонецФункции

// Проверяет, сопоставлен ли пользователь ИБ с элементом справочника Пользователи или
// с элементом справочника ВнешниеПользователи.
// 
// Параметры:
//  ПользовательИБ - Строка - имя пользователя ИБ.
//                 - УникальныйИдентификатор - уникальный идентификатор пользователя ИБ.
//                 - ПользовательИнформационнойБазы
//
//  УчетнаяЗапись  - ПользовательИнформационнойБазы - возвращаемое значение.
//
// Возвращаемое значение:
//  Булево - Истина, если пользователь ИБ существует и его идентификатор
//   используется либо в справочнике Пользователи, либо в справочнике ВнешниеПользователи.
//
Функция ПользовательИБЗанят(ПользовательИБ, УчетнаяЗапись = Неопределено) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Если ТипЗнч(ПользовательИБ) = Тип("Строка") Тогда
		УчетнаяЗапись = ПользователиИнформационнойБазы.НайтиПоИмени(ПользовательИБ);
		
	ИначеЕсли ТипЗнч(ПользовательИБ) = Тип("УникальныйИдентификатор") Тогда
		УчетнаяЗапись = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(ПользовательИБ);
	Иначе
		УчетнаяЗапись = ПользовательИБ;
	КонецЕсли;
	
	Если УчетнаяЗапись = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Возврат ПользователиСлужебный.ПользовательПоИдентификаторуСуществует(
		УчетнаяЗапись.УникальныйИдентификатор);
	
КонецФункции

// Возвращает пустую структуру описания пользователя ИБ.
// Назначение свойств структуры соответствует свойствам объекта ПользовательИнформационнойБазы.
//
// Возвращаемое значение:
//  Структура:
//   * УникальныйИдентификатор   - УникальныйИдентификатор - уникальный идентификатор пользователя информационной базы.
//   * Имя                       - Строка - имя пользователя информационной базы. Например, "Иванов".
//   * ПолноеИмя                 - Строка - полное имя пользователя информационной базы. 
//                                          Например, "Иванов Иван Иванович (менеджер по продажам)".
//   * АдресЭлектроннойПочты     - Строка - адрес электронной почты (например, для восстановления пароля).
//
//   * АутентификацияСтандартная      - Булево - разрешена ли стандартная аутентификация (по пользователю и паролю).
//   * ПоказыватьВСпискеВыбора        - Булево - показывать ли полное имя пользователя в списке для выбора при запуске.
//   * Пароль                         - Строка - пароль, используемый при стандартной аутентификации.
//                                    - Неопределено - после чтения, при установке означает не менять пароль.
//   * СохраняемоеЗначениеПароля      - Строка - хранимое значение пароля (хеш).
//                                    - Неопределено - при установке означает не менять сохраняемое значение.
//   * ПарольУстановлен               - Булево - установлен ли пароль у пользователя.
//   * ЗапрещеноИзменятьПароль        - Булево - определяет возможность пользователя изменять свой пароль.
//   * ЗапрещеноВосстанавливатьПароль - Булево - определяет возможность пользователя восстанавливать свой пароль.
//
//   * АутентификацияOpenID         - Булево - разрешена ли аутентификация по протоколу OpenID.
//   * АутентификацияOpenIDConnect  - Булево - разрешена ли аутентификация по протоколу OpenID-Connect.
//   * АутентификацияТокеномДоступа - Булево - разрешена ли аутентификация JWT-токеном.
//
//   * АутентификацияОС          - Булево - разрешена ли аутентификация средствами операционной системы.
//   * ПользовательОС            - Строка - имя соответствующей учетной записи пользователя операционной системы 
//                                          (не учитывается в учебной версии платформы).
//
//   * ОсновнойИнтерфейс         - Строка - имя основного интерфейса пользователя информационной базы
//                                         (из коллекции Метаданные.Интерфейсы).
//                               - Неопределено
//   * РежимЗапуска              - Строка - "Авто", "ОбычноеПриложение" или "УправляемоеПриложение".
//                               - Неопределено
//   * Язык                      - Строка - имя языка из коллекции Метаданные.Языки.
//                               - Неопределено
//   * Роли                      - Массив - коллекция имен ролей пользователя информационной базы.
//                               - Неопределено - роли не указаны.
//
//   * ЗащитаОтОпасныхДействий   - Булево - по умолчанию Истина. Соответствует
//                                   свойству ПредупреждатьОбОпасныхДействиях типа ЗащитаОтОпасныхДействий.
//
Функция НовоеОписаниеПользователяИБ() Экспорт
	
	// Подготовка структур возвращаемых данных.
	Свойства = Новый Структура;
	
	Свойства.Вставить("УникальныйИдентификатор", ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
	
	Свойства.Вставить("Имя",                            "");
	Свойства.Вставить("ПолноеИмя",                      "");
	Свойства.Вставить("АдресЭлектроннойПочты",          "");
	Свойства.Вставить("АутентификацияСтандартная",      Ложь);
	Свойства.Вставить("ПоказыватьВСпискеВыбора",        Ложь);
	Свойства.Вставить("СтарыйПароль",                   Неопределено);
	Свойства.Вставить("Пароль",                         Неопределено);
	Свойства.Вставить("СохраняемоеЗначениеПароля",      Неопределено);
	Свойства.Вставить("ПарольУстановлен",               Ложь);
	Свойства.Вставить("ЗапрещеноИзменятьПароль",        Ложь);
	Свойства.Вставить("ЗапрещеноВосстанавливатьПароль", Истина);
	Свойства.Вставить("АутентификацияOpenID",           Ложь);
	Свойства.Вставить("АутентификацияOpenIDConnect",    Ложь);
	Свойства.Вставить("АутентификацияТокеномДоступа",   Ложь);
	Свойства.Вставить("АутентификацияОС",               Ложь);
	Свойства.Вставить("ПользовательОС",                 "");
	
	Свойства.Вставить("ОсновнойИнтерфейс",
		?(Метаданные.ОсновнойИнтерфейс = Неопределено, "", Метаданные.ОсновнойИнтерфейс.Имя));
	
	Свойства.Вставить("РежимЗапуска",              "Авто");
	
	Свойства.Вставить("Язык",
		?(Метаданные.ОсновнойЯзык = Неопределено, "", Метаданные.ОсновнойЯзык.Имя));
	
	Свойства.Вставить("Роли", Неопределено);
	
	Свойства.Вставить("ЗащитаОтОпасныхДействий", Истина);
	
	Возврат Свойства;
	
КонецФункции

// Возвращает свойства пользователя информационной базы в виде структуры.
// Если пользователь с указанным идентификатором или именем не существует, то возвращается Неопределено.
//
// Параметры:
//  ИмяИлиИдентификатор  - Строка
//                       - УникальныйИдентификатор - имя или идентификатор пользователя ИБ.
//
// Возвращаемое значение:
//  Структура, Неопределено - свойства пользователя, см. Пользователи.НовоеОписаниеПользователяИБ.
//                            Неопределено, если пользователь с указанным идентификатором или именем не существует.
//
Функция СвойстваПользователяИБ(Знач ИмяИлиИдентификатор) Экспорт
	
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("Пользователи.СвойстваПользователяИБ", "ИмяИлиИдентификатор",
		ИмяИлиИдентификатор, Новый ОписаниеТипов("Строка, УникальныйИдентификатор"));
		 
	Свойства = НовоеОписаниеПользователяИБ();
	Свойства.Роли = Новый Массив;
	
	Если ТипЗнч(ИмяИлиИдентификатор) = Тип("УникальныйИдентификатор") Тогда
		ПользовательИБ = ПользователиСлужебный.ПользовательИБПоИдентификатору(ИмяИлиИдентификатор);
		
	ИначеЕсли ТипЗнч(ИмяИлиИдентификатор) = Тип("Строка") Тогда
		ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоИмени(ИмяИлиИдентификатор);
	Иначе
		ПользовательИБ = Неопределено;
	КонецЕсли;
	
	Если ПользовательИБ = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	СкопироватьСвойстваПользователяИБ(Свойства, ПользовательИБ);
	Свойства.Вставить("ПользовательИБ", ПользовательИБ);
	Возврат Свойства;
	
КонецФункции

// Записывает новые значения свойств указанного пользователя ИБ либо создает нового пользователя ИБ.
// Если пользователь не существует, а также при попытке создания существующего пользователя будет вызвано исключение.
//
// Параметры:
//  ИмяИлиИдентификатор - Строка
//                      - УникальныйИдентификатор - имя или уникальный идентификатор пользователя ИБ, свойства 
//                                                  которого нужно установить. Либо имя нового пользователя ИБ.
//  ОбновляемыеСвойства - см. Пользователи.НовоеОписаниеПользователяИБ.
//
//  СоздатьНового - Булево - указать Истина, чтобы создать нового пользователя ИБ с именем ИмяИлиИдентификатор.
//
//  ЭтоВнешнийПользователь - Булево - указать Истина, если пользователь ИБ соответствует внешнему пользователю
//                                    (элементу справочника ВнешниеПользователи).
//
Процедура УстановитьСвойстваПользователяИБ(Знач ИмяИлиИдентификатор, Знач ОбновляемыеСвойства,
	Знач СоздатьНового = Ложь, Знач ЭтоВнешнийПользователь = Ложь) Экспорт
	
	ИмяПроцедуры = "Пользователи.УстановитьСвойстваПользователяИБ";
	
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ИмяИлиИдентификатор",
		ИмяИлиИдентификатор, Новый ОписаниеТипов("Строка, УникальныйИдентификатор"));
	
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ОбновляемыеСвойства",
		ОбновляемыеСвойства, Тип("Структура"));
	
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "СоздатьНового",
		СоздатьНового, Тип("Булево"));
	
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ЭтоВнешнийПользователь",
		ЭтоВнешнийПользователь, Тип("Булево"));
	
	СтарыеСвойства = СвойстваПользователяИБ(ИмяИлиИдентификатор);
	ПользовательСуществует = СтарыеСвойства <> Неопределено;
	Если ПользовательСуществует Тогда
		ПользовательИБ = СтарыеСвойства.ПользовательИБ;
	Иначе
		ПользовательИБ = Неопределено;
		СтарыеСвойства = НовоеОписаниеПользователяИБ();
	КонецЕсли;
		
	Если Не ПользовательСуществует Тогда
		Если Не СоздатьНового Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Пользователь информационной базы ""%1"" не существует.'"),
				ИмяИлиИдентификатор);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		ПользовательИБ = ПользователиИнформационнойБазы.СоздатьПользователя();
	Иначе
		Если СоздатьНового Тогда
			ТекстОшибки = ОписаниеОшибкиПриЗаписиПользователяИБ(
				НСтр("ru = 'Невозможно создать пользователя информационной базы %1, так как он уже существует.'"),
				СтарыеСвойства.Имя,
				СтарыеСвойства.УникальныйИдентификатор);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		
		Если ОбновляемыеСвойства.Свойство("СтарыйПароль")
		   И ТипЗнч(ОбновляемыеСвойства.СтарыйПароль) = Тип("Строка") Тогда
			
			СтарыйПарольСовпадает = ПользователиСлужебный.СтарыйПарольСовпадаетССохраненным(
				ОбновляемыеСвойства.СтарыйПароль, СтарыеСвойства.УникальныйИдентификатор);
			
			Если Не СтарыйПарольСовпадает Тогда
				ТекстОшибки = ОписаниеОшибкиПриЗаписиПользователяИБ(
					НСтр("ru = 'При записи пользователя информационной базы %1 старый пароль указан не верно.'"),
					СтарыеСвойства.Имя,
					СтарыеСвойства.УникальныйИдентификатор);
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	// Подготовка новых значений свойств.
	УстановитьПароль = Ложь;
	НовыеСвойства = ОбщегоНазначения.СкопироватьРекурсивно(СтарыеСвойства);
	Для Каждого КлючИЗначение Из НовыеСвойства Цикл
		Если Не ОбновляемыеСвойства.Свойство(КлючИЗначение.Ключ)
		 Или ОбновляемыеСвойства[КлючИЗначение.Ключ] = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		Если КлючИЗначение.Ключ <> "Пароль" Тогда
			НовыеСвойства[КлючИЗначение.Ключ] = ОбновляемыеСвойства[КлючИЗначение.Ключ];
			Продолжить;
		КонецЕсли;
		Если ОбновляемыеСвойства.Свойство("СохраняемоеЗначениеПароля")
		   И ОбновляемыеСвойства.СохраняемоеЗначениеПароля <> Неопределено
		 Или СтандартныеПодсистемыСервер.ЭтоУчебнаяПлатформа() Тогда
			Продолжить;
		КонецЕсли;
		УстановитьПароль = Истина;
	КонецЦикла;
	
	СкопироватьСвойстваПользователяИБ(ПользовательИБ, НовыеСвойства);
	
	ПользователиСлужебный.УстановитьПолитикуПаролей(ПользовательИБ, ЭтоВнешнийПользователь);
	
	Если УстановитьПароль Тогда
		ТекстОшибкиПароля = ПользователиСлужебный.ОшибкаСоответствияПароляТребованиям(
			ОбновляемыеСвойства.Пароль, ПользовательИБ);
		
		Если ЗначениеЗаполнено(ТекстОшибкиПароля) Тогда
			ТекстОшибки = ОписаниеОшибкиПриЗаписиПользователяИБ(
				НСтр("ru = 'Не удалось записать свойства пользователя информационной базы %1 по причине:
				           |%2.'"),
				ПользовательИБ.Имя,
				?(ПользовательСуществует, СтарыеСвойства.УникальныйИдентификатор, Неопределено),
				ТекстОшибкиПароля);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		ПользовательИБ.СохраняемоеЗначениеПароля =
			ПользователиСлужебный.СохраняемоеЗначениеСтрокиПароля(ОбновляемыеСвойства.Пароль, Истина);
	КонецЕсли;
	
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		ПользовательИБ.ПоказыватьВСпискеВыбора = Ложь;
	КонецЕсли;
	
	// Попытка записи нового или изменение существующего пользователя ИБ.
	Попытка
		ПользователиСлужебный.ЗаписатьПользователяИнформационнойБазы(ПользовательИБ, ЭтоВнешнийПользователь);
	Исключение
		ТекстОшибки = ОписаниеОшибкиПриЗаписиПользователяИБ(
			НСтр("ru = 'Не удалось записать свойства пользователя информационной базы %1 по причине:
			           |%2.'"),
			ПользовательИБ.Имя,
			?(ПользовательСуществует, СтарыеСвойства.УникальныйИдентификатор, Неопределено),
			ИнформацияОбОшибке());
		ВызватьИсключение ТекстОшибки;
	КонецПопытки;
	
	Если ЗначениеЗаполнено(СтарыеСвойства.Имя) И СтарыеСвойства.Имя <> НовыеСвойства.Имя Тогда
		// Перемещение настроек пользователя.
		ПользователиСлужебный.СкопироватьНастройкиПользователя(СтарыеСвойства.Имя, НовыеСвойства.Имя, Истина);
	КонецЕсли;
	
	Если СоздатьНового Тогда
		ПользователиСлужебный.УстановитьНачальныеНастройки(ПользовательИБ.Имя, ЭтоВнешнийПользователь);
	КонецЕсли;
	
	ПользователиПереопределяемый.ПриЗаписиПользователяИнформационнойБазы(СтарыеСвойства, НовыеСвойства);
	ОбновляемыеСвойства.Вставить("УникальныйИдентификатор", ПользовательИБ.УникальныйИдентификатор);
	ОбновляемыеСвойства.Вставить("ПользовательИБ", ПользовательИБ);
	
КонецПроцедуры

// Удаляет указанного пользователя информационной базы.
//
// Параметры:
//  ИмяИлиИдентификатор  - Строка
//                       - УникальныйИдентификатор - имя или идентификатор удаляемого пользователя ИБ.
//
Процедура УдалитьПользователяИБ(Знач ИмяИлиИдентификатор) Экспорт
	
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("Пользователи.УдалитьПользователяИБ", "ИмяИлиИдентификатор",
		ИмяИлиИдентификатор, Новый ОписаниеТипов("Строка, УникальныйИдентификатор"));
		
	СвойстваУдаляемогоПользователяИБ = СвойстваПользователяИБ(ИмяИлиИдентификатор);
	Если СвойстваУдаляемогоПользователяИБ = Неопределено Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Пользователь информационной базы ""%1"" не существует.'"),
			ИмяИлиИдентификатор);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	ПользовательИБ = СвойстваУдаляемогоПользователяИБ.ПользовательИБ;
		
	Попытка
		
		ИнтеграцияПодсистемБСП.ПередУдалениемПользователяИБ(ПользовательИБ);
		ПользовательИБ.Удалить();
		
	Исключение
		ТекстОшибки = ОписаниеОшибкиПриЗаписиПользователяИБ(
			НСтр("ru = 'Не удалось удалить пользователя информационной базы %1 по причине:
			           |%2.'"),
			ПользовательИБ.Имя,
			ПользовательИБ.УникальныйИдентификатор,
			ИнформацияОбОшибке());
		ВызватьИсключение ТекстОшибки;
	КонецПопытки;
	ПользователиПереопределяемый.ПослеУдаленияПользователяИнформационнойБазы(СвойстваУдаляемогоПользователяИБ);
	
КонецПроцедуры

// Копирует значения свойств пользователя ИБ с преобразованием
// в/из строковых идентификаторов для основного интерфейса,
// языка, режима запуска и ролей.
//
//  Несуществующие свойства в источнике или приемнике не копируются.
//
//  Свойства Пароль и СохраняемоеЗначениеПароля не копируются, если
// значение в Источнике равно Неопределено.
//
//  Свойства АутентификацияСтандартная, АутентификацияOpenID,
// АутентификацияOpenIDConnect, АутентификацияТокеномДоступа,
// АутентификацияОС, ПользовательОС и СохраняемоеЗначениеПароля не переустанавливаются,
// если совпадают, когда Приемник типа ПользовательИнформационнойБазы.
//
//  Свойства УникальныйИдентификатор, ПарольУстановлен, СтарыйПароль
// не копируются, если Приемник типа ПользовательИнформационнойБазы.
//
//  Преобразование выполняется только для Источника или Приемника
// с типом ПользовательИнформационнойБазы.
//
// Параметры:
//  Приемник     - Структура
//               - ПользовательИнформационнойБазы
//               - ФормаКлиентскогоПриложения - подмножество
//                 свойств из НовоеОписаниеПользователяИБ().
//
//  Источник     - Структура
//               - ПользовательИнформационнойБазы
//               - ФормаКлиентскогоПриложения - как и приемник,
//                 но типы обратные, т.е. когда Приемник типа ПользовательИнформационнойБазы,
//                 тогда в Источник не типа ПользовательИнформационнойБазы.
// 
//  КопируемыеСвойства  - Строка - список свойств через запятую, которые нужно скопировать (без префикса).
//  ИсключаемыеСвойства - Строка - список свойств через запятую, которые не нужно копировать (без префикса).
//  ПрефиксСвойств      - Строка - начальное имя для Источника или Приемника типа НЕ Структура.
//
Процедура СкопироватьСвойстваПользователяИБ(Приемник,
                                            Источник,
                                            КопируемыеСвойства = "",
                                            ИсключаемыеСвойства = "",
                                            ПрефиксСвойств = "") Экспорт
	
	Если ТипЗнч(Приемник) = Тип("ПользовательИнформационнойБазы")
	   И ТипЗнч(Источник) = Тип("ПользовательИнформационнойБазы")
	   
	 Или ТипЗнч(Приемник) = Тип("ПользовательИнформационнойБазы")
	   И ТипЗнч(Источник) <> Тип("Структура")
	   И ТипЗнч(Источник) <> Тип("ФормаКлиентскогоПриложения")
	   
	 Или ТипЗнч(Источник) = Тип("ПользовательИнформационнойБазы")
	   И ТипЗнч(Приемник) <> Тип("Структура")
	   И ТипЗнч(Приемник) <> Тип("ФормаКлиентскогоПриложения") Тогда
		
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Недопустимое значение параметра %1 или %2
			           |в процедуре %3 общего модуля %4.'"),
			"Приемник",
			"Источник",
			"СкопироватьСвойстваПользователяИБ",
			"Пользователи");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	ВсеСвойства = НовоеОписаниеПользователяИБ();
	
	Если ЗначениеЗаполнено(КопируемыеСвойства) Тогда
		СтруктураКопируемыхСвойств = Новый Структура(КопируемыеСвойства);
	Иначе
		СтруктураКопируемыхСвойств = ВсеСвойства;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ИсключаемыеСвойства) Тогда
		СтруктураИсключаемыхСвойств = Новый Структура(ИсключаемыеСвойства);
	Иначе
		СтруктураИсключаемыхСвойств = Новый Структура;
	КонецЕсли;
	
	Если СтандартныеПодсистемыСервер.ЭтоУчебнаяПлатформа() Тогда
		СтруктураИсключаемыхСвойств.Вставить("АутентификацияОС");
		СтруктураИсключаемыхСвойств.Вставить("ПользовательОС");
	КонецЕсли;
	
	ПарольУстановлен = Ложь;
	
	Для Каждого КлючИЗначение Из ВсеСвойства Цикл
		Свойство = КлючИЗначение.Ключ;
		
		Если НЕ СтруктураКопируемыхСвойств.Свойство(Свойство)
		 Или СтруктураИсключаемыхСвойств.Свойство(Свойство) Тогда
		
			Продолжить;
		КонецЕсли;
		
		Если ТипЗнч(Источник) = Тип("ПользовательИнформационнойБазы")
		   И (    ТипЗнч(Приемник) = Тип("Структура")
		      Или ТипЗнч(Приемник) = Тип("ФормаКлиентскогоПриложения") ) Тогда
			
			Если Свойство = "Пароль"
			 Или Свойство = "СтарыйПароль" Тогда
				
				ЗначениеСвойства = Неопределено;
				
			ИначеЕсли Свойство = "ОсновнойИнтерфейс" Тогда
				ЗначениеСвойства = ?(Источник.ОсновнойИнтерфейс = Неопределено,
				                     "",
				                     Источник.ОсновнойИнтерфейс.Имя);
			
			ИначеЕсли Свойство = "РежимЗапуска" Тогда
				ПолноеИмяЗначения = ПолучитьПолноеИмяПредопределенногоЗначения(Источник.РежимЗапуска);
				ЗначениеСвойства = Сред(ПолноеИмяЗначения, СтрНайти(ПолноеИмяЗначения, ".") + 1);
				
			ИначеЕсли Свойство = "Язык" Тогда
				ЗначениеСвойства = ?(Источник.Язык = Неопределено,
				                     "",
				                     Источник.Язык.Имя);
				
			ИначеЕсли Свойство = "ЗащитаОтОпасныхДействий" Тогда
				ЗначениеСвойства =
					Источник.ЗащитаОтОпасныхДействий.ПредупреждатьОбОпасныхДействиях;
				
			ИначеЕсли Свойство = "Роли" Тогда
				
				ВременнаяСтруктура = Новый Структура("Роли", Новый ТаблицаЗначений);
				ЗаполнитьЗначенияСвойств(ВременнаяСтруктура, Приемник);
				Если ТипЗнч(ВременнаяСтруктура.Роли) = Тип("ТаблицаЗначений") Тогда
					Продолжить;
				ИначеЕсли ВременнаяСтруктура.Роли = Неопределено Тогда
					Приемник.Роли = Новый Массив;
				Иначе
					Приемник.Роли.Очистить();
				КонецЕсли;
				
				Для каждого Роль Из Источник.Роли Цикл
					Приемник.Роли.Добавить(Роль.Имя);
				КонецЦикла;
				
				Продолжить;
			Иначе
				ЗначениеСвойства = Источник[Свойство];
			КонецЕсли;
			
			ПолноеИмяСвойства = ПрефиксСвойств + Свойство;
			ВременнаяСтруктура = Новый Структура(ПолноеИмяСвойства, ЗначениеСвойства);
			ЗаполнитьЗначенияСвойств(Приемник, ВременнаяСтруктура);
		Иначе
			Если ТипЗнч(Источник) = Тип("Структура") Тогда
				Если Источник.Свойство(Свойство) Тогда
					ЗначениеСвойства = Источник[Свойство];
				Иначе
					Продолжить;
				КонецЕсли;
			Иначе
				ПолноеИмяСвойства = ПрефиксСвойств + Свойство;
				ВременнаяСтруктура = Новый Структура(ПолноеИмяСвойства, Новый ТаблицаЗначений);
				ЗаполнитьЗначенияСвойств(ВременнаяСтруктура, Источник);
				ЗначениеСвойства = ВременнаяСтруктура[ПолноеИмяСвойства];
				Если ТипЗнч(ЗначениеСвойства) = Тип("ТаблицаЗначений") Тогда
					Продолжить;
				КонецЕсли;
			КонецЕсли;
			
			Если ТипЗнч(Приемник) = Тип("ПользовательИнформационнойБазы") Тогда
			
				Если Свойство = "УникальныйИдентификатор"
				 Или Свойство = "СтарыйПароль"
				 Или Свойство = "ПарольУстановлен" Тогда
					
					Продолжить;
					
				ИначеЕсли Свойство = "АутентификацияСтандартная"
				      Или Свойство = "АутентификацияOpenID"
				      Или Свойство = "АутентификацияOpenIDConnect"
				      Или Свойство = "АутентификацияТокеномДоступа"
				      Или Свойство = "АутентификацияОС"
				      Или Свойство = "ПользовательОС" Тогда
					
					Если Приемник[Свойство] <> ЗначениеСвойства Тогда
						Приемник[Свойство] = ЗначениеСвойства;
					КонецЕсли;
					
				ИначеЕсли Свойство = "Пароль" Тогда
					Если ЗначениеСвойства <> Неопределено Тогда
						Приемник.Пароль = ЗначениеСвойства;
						ПарольУстановлен = Истина;
					КонецЕсли;
					
				ИначеЕсли Свойство = "СохраняемоеЗначениеПароля" Тогда
					Если ЗначениеСвойства <> Неопределено
					   И Не ПарольУстановлен
					   И Приемник.СохраняемоеЗначениеПароля <> ЗначениеСвойства Тогда
						Приемник.СохраняемоеЗначениеПароля = ЗначениеСвойства;
					КонецЕсли;
					
				ИначеЕсли Свойство = "ОсновнойИнтерфейс" Тогда
					Если ТипЗнч(ЗначениеСвойства) = Тип("Строка") Тогда
						Приемник.ОсновнойИнтерфейс = Метаданные.Интерфейсы.Найти(ЗначениеСвойства);
					Иначе
						Приемник.ОсновнойИнтерфейс = Неопределено;
					КонецЕсли;
				
				ИначеЕсли Свойство = "РежимЗапуска" Тогда
					Если ЗначениеСвойства = "Авто"
					 Или ЗначениеСвойства = "ОбычноеПриложение"
					 Или ЗначениеСвойства = "УправляемоеПриложение" Тогда
						
						Приемник.РежимЗапуска = РежимЗапускаКлиентскогоПриложения[ЗначениеСвойства];
					Иначе
						Приемник.РежимЗапуска = РежимЗапускаКлиентскогоПриложения.Авто;
					КонецЕсли;
					
				ИначеЕсли Свойство = "ЗащитаОтОпасныхДействий" Тогда
					Приемник.ЗащитаОтОпасныхДействий.ПредупреждатьОбОпасныхДействиях =
						ЗначениеСвойства;
					
				ИначеЕсли Свойство = "Язык" Тогда
					Если ТипЗнч(ЗначениеСвойства) = Тип("Строка") Тогда
						Приемник.Язык = Метаданные.Языки.Найти(ЗначениеСвойства);
					Иначе
						Приемник.Язык = Неопределено;
					КонецЕсли;
					
				ИначеЕсли Свойство = "Роли" Тогда
					Приемник.Роли.Очистить();
					Если ЗначениеСвойства <> Неопределено Тогда
						Для Каждого ИмяРоли Из ЗначениеСвойства Цикл
							Роль = Метаданные.Роли.Найти(ИмяРоли);
							Если Роль <> Неопределено Тогда
								Приемник.Роли.Добавить(Роль);
							КонецЕсли;
						КонецЦикла;
					КонецЕсли;
				Иначе
					Если Свойство = "Имя"
					   И Приемник[Свойство] <> ЗначениеСвойства Тогда
					
						Если СтрДлина(ЗначениеСвойства) > 64 Тогда
							ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
								НСтр("ru = 'Ошибка записи пользователя информационной базы
								           |Имя (для входа): ""%1""
								           |превышает длину 64 символа.'"),
								ЗначениеСвойства);
							ВызватьИсключение ТекстОшибки;
							
						ИначеЕсли СтрНайти(ЗначениеСвойства, ":") > 0 Тогда
							ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
								НСтр("ru = 'Ошибка записи пользователя информационной базы
								           |Имя (для входа): ""%1""
								           |содержит запрещенный символ "":"".'"),
								ЗначениеСвойства);
							ВызватьИсключение ТекстОшибки;
						КонецЕсли;
					КонецЕсли;
					Приемник[Свойство] = Источник[Свойство];
				КонецЕсли;
			Иначе
				Если Свойство = "Роли" Тогда
					
					ВременнаяСтруктура = Новый Структура("Роли", Новый ТаблицаЗначений);
					ЗаполнитьЗначенияСвойств(ВременнаяСтруктура, Приемник);
					Если ТипЗнч(ВременнаяСтруктура.Роли) = Тип("ТаблицаЗначений") Тогда
						Продолжить;
					ИначеЕсли ВременнаяСтруктура.Роли = Неопределено Тогда
						Приемник.Роли = Новый Массив;
					Иначе
						Приемник.Роли.Очистить();
					КонецЕсли;
					
					Если Источник.Роли <> Неопределено Тогда
						Для каждого Роль Из Источник.Роли Цикл
							Приемник.Роли.Добавить(Роль.Имя);
						КонецЦикла;
					КонецЕсли;
					Продолжить;
					
				ИначеЕсли ТипЗнч(Источник) = Тип("Структура") Тогда
					ПолноеИмяСвойства = ПрефиксСвойств + Свойство;
				Иначе
					ПолноеИмяСвойства = Свойство;
				КонецЕсли;
				ВременнаяСтруктура = Новый Структура(ПолноеИмяСвойства, ЗначениеСвойства);
				ЗаполнитьЗначенияСвойств(Приемник, ВременнаяСтруктура);
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Возвращает пользователя из справочника Пользователи или ВнешниеПользователи,
// с которым сопоставлен пользователь ИБ с указанным именем.
// 
// Параметры:
//  ИмяДляВхода - Строка - имя пользователя информационной базы, используемое для входа.
//
// Возвращаемое значение:
//  СправочникСсылка.Пользователи           - если пользователь найден.
//  СправочникСсылка.ВнешниеПользователи    - если внешний пользователь найден.
//  Справочники.Пользователи.ПустаяСсылка() - если пользователь ИБ найден.
//  Неопределено                            - если пользователь ИБ не существует.
//
Функция НайтиПоИмени(Знач ИмяДляВхода) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоИмени(ИмяДляВхода);
	Если ПользовательИБ = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Пользователь = НайтиПоИдентификатору(ПользовательИБ.УникальныйИдентификатор);
	Если Пользователь = Неопределено Тогда
		Пользователь = ПредопределенноеЗначение("Справочник.Пользователи.ПустаяСсылка");
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Ложь);
	
	Возврат Пользователь;
	
КонецФункции

// Возвращает пользователя из справочника Пользователи или ВнешниеПользователи,
// с которым сопоставлен пользователь ИБ с указанным уникальным идентификатором.
// Поиск выполняется без учета существования пользователя информационной базы.
// 
// Параметры:
//  ИдентификаторПользователяИБ - УникальныйИдентификатор - идентификатор пользователя информационной базы.
//
// Возвращаемое значение:
//  СправочникСсылка.Пользователи           - если пользователь найден.
//  СправочникСсылка.ВнешниеПользователи    - если внешний пользователь найден.
//  Неопределено                            - если пользователь не найден ни в одном справочнике.
//
Функция НайтиПоИдентификатору(Знач ИдентификаторПользователяИБ) Экспорт
	
	Если ТипЗнч(ИдентификаторПользователяИБ) <> Тип("УникальныйИдентификатор") Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Пользователь = Неопределено;
	
	УстановитьПривилегированныйРежим(Истина);
	ПользователиСлужебный.ПользовательПоИдентификаторуСуществует(
		ИдентификаторПользователяИБ,, Пользователь);
	УстановитьПривилегированныйРежим(Ложь);
	
	Возврат Пользователь;
	
КонецФункции

// Возвращает пользователя ИБ по ссылке справочника Пользователи или ВнешниеПользователи.
//  Для поиска требуются административные права. Если административных прав нет,
// допустимо искать только пользователя для текущего пользователя ИБ.
// 
// Параметры:
//  Пользователь - СправочникСсылка.Пользователи, СправочникСсылка.ВнешниеПользователи 
//
// Возвращаемое значение:
//  ПользовательИнформационнойБазы - если найден.
//  Неопределено - если пользователь ИБ не существует.
//
Функция НайтиПоСсылке(Пользователь) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	ИдентификаторПользователяИБ = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Пользователь,
		"ИдентификаторПользователяИБ");
	УстановитьПривилегированныйРежим(Ложь);
	
	Если ТипЗнч(ИдентификаторПользователяИБ) <> Тип("УникальныйИдентификатор") Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Возврат ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(ИдентификаторПользователяИБ);
	
КонецФункции

// Выполняет поиск идентификаторов пользователей ИБ, используемых более одного раза, и
// либо вызывает исключение, либо возвращает найденных пользователей ИБ для дальнейшей
// обработки.
//
// Параметры:
//  Пользователь - Неопределено - проверка для всех пользователей и внешних пользователей.
//               - СправочникСсылка.Пользователи
//               - СправочникСсылка.ВнешниеПользователи - проверка
//                 только для указанной ссылки.
//
//  УникальныйИдентификатор - Неопределено - проверка всех заданных идентификаторов пользователей ИБ.
//                          - УникальныйИдентификатор - проверка только для заданного идентификатора.
//
//  НайденныеИдентификаторы - Неопределено - при нахождении ошибок вызывается исключение, но если
//                            передать соответствие, тогда при нахождении ошибок исключение не вызывается,
//                            а вместо этого заполняется переданное соответствие.
//                          - Соответствие из КлючИЗначение:
//                              * Ключ     - УникальныйИдентификатор - неоднозначный идентификатор пользователя ИБ.
//                              * Значение - Массив из СправочникСсылка.Пользователи, СправочникСсылка.ВнешниеПользователи
//
//  ИдентификаторПользователяСервиса - Булево - если Ложь, тогда проверять ИдентификаторПользователяИБ,
//                                              если Истина, тогда проверять ИдентификаторПользователяСервиса.
//
Процедура НайтиНеоднозначныхПользователейИБ(Знач Пользователь,
                                            Знач УникальныйИдентификатор = Неопределено,
                                            Знач НайденныеИдентификаторы = Неопределено,
                                            Знач ИдентификаторПользователяСервиса = Ложь) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	ПустойУникальныйИдентификатор = ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор();
	
	Если ТипЗнч(УникальныйИдентификатор) <> Тип("УникальныйИдентификатор")
	 Или УникальныйИдентификатор = ПустойУникальныйИдентификатор Тогда
		
		УникальныйИдентификатор = Неопределено;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ПустойУникальныйИдентификатор", ПустойУникальныйИдентификатор);
	
	Если Пользователь = Неопределено И УникальныйИдентификатор = Неопределено Тогда
		Запрос.Текст =
		"ВЫБРАТЬ
		|	Пользователи.ИдентификаторПользователяИБ КАК НеоднозначныйИдентификатор
		|ИЗ
		|	Справочник.Пользователи КАК Пользователи
		|
		|СГРУППИРОВАТЬ ПО
		|	Пользователи.ИдентификаторПользователяИБ
		|
		|ИМЕЮЩИЕ
		|	Пользователи.ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор И
		|	КОЛИЧЕСТВО(Пользователи.Ссылка) > 1
		|
		|ОБЪЕДИНИТЬ ВСЕ
		|
		|ВЫБРАТЬ
		|	ВнешниеПользователи.ИдентификаторПользователяИБ
		|ИЗ
		|	Справочник.ВнешниеПользователи КАК ВнешниеПользователи
		|
		|СГРУППИРОВАТЬ ПО
		|	ВнешниеПользователи.ИдентификаторПользователяИБ
		|
		|ИМЕЮЩИЕ
		|	ВнешниеПользователи.ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор И
		|	КОЛИЧЕСТВО(ВнешниеПользователи.Ссылка) > 1
		|
		|ОБЪЕДИНИТЬ ВСЕ
		|
		|ВЫБРАТЬ
		|	Пользователи.ИдентификаторПользователяИБ
		|ИЗ
		|	Справочник.Пользователи КАК Пользователи
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ВнешниеПользователи КАК ВнешниеПользователи
		|		ПО (ВнешниеПользователи.ИдентификаторПользователяИБ = Пользователи.ИдентификаторПользователяИБ)
		|			И (Пользователи.ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор)";
		
	ИначеЕсли УникальныйИдентификатор <> Неопределено Тогда
		
		Запрос.УстановитьПараметр("УникальныйИдентификатор", УникальныйИдентификатор);
		Запрос.Текст =
		"ВЫБРАТЬ
		|	Пользователи.ИдентификаторПользователяИБ КАК НеоднозначныйИдентификатор
		|ИЗ
		|	Справочник.Пользователи КАК Пользователи
		|ГДЕ
		|	Пользователи.ИдентификаторПользователяИБ = &УникальныйИдентификатор
		|
		|ОБЪЕДИНИТЬ ВСЕ
		|
		|ВЫБРАТЬ
		|	ВнешниеПользователи.ИдентификаторПользователяИБ
		|ИЗ
		|	Справочник.ВнешниеПользователи КАК ВнешниеПользователи
		|ГДЕ
		|	ВнешниеПользователи.ИдентификаторПользователяИБ = &УникальныйИдентификатор";
	Иначе
		Запрос.УстановитьПараметр("Пользователь", Пользователь);
		Запрос.Текст =
		"ВЫБРАТЬ
		|	Пользователи.ИдентификаторПользователяИБ КАК НеоднозначныйИдентификатор
		|ИЗ
		|	Справочник.Пользователи КАК Пользователи
		|ГДЕ
		|	Пользователи.ИдентификаторПользователяИБ В
		|			(ВЫБРАТЬ
		|				СправочникПользователи.ИдентификаторПользователяИБ
		|			ИЗ
		|				Справочник.Пользователи КАК СправочникПользователи
		|			ГДЕ
		|				СправочникПользователи.Ссылка = &Пользователь
		|				И СправочникПользователи.ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор)
		|
		|ОБЪЕДИНИТЬ ВСЕ
		|
		|ВЫБРАТЬ
		|	ВнешниеПользователи.ИдентификаторПользователяИБ
		|ИЗ
		|	Справочник.ВнешниеПользователи КАК ВнешниеПользователи
		|ГДЕ
		|	ВнешниеПользователи.ИдентификаторПользователяИБ В
		|			(ВЫБРАТЬ
		|				СправочникПользователи.ИдентификаторПользователяИБ
		|			ИЗ
		|				Справочник.Пользователи КАК СправочникПользователи
		|			ГДЕ
		|				СправочникПользователи.Ссылка = &Пользователь
		|				И СправочникПользователи.ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор)";
		
		Если ТипЗнч(Пользователь) = Тип("СправочникСсылка.ВнешниеПользователи") Тогда
			Запрос.Текст = СтрЗаменить(Запрос.Текст,
				"Справочник.Пользователи КАК СправочникПользователи",
				"Справочник.ВнешниеПользователи КАК СправочникПользователи");
		КонецЕсли;
	КонецЕсли;
	
	Если ИдентификаторПользователяСервиса Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"ИдентификаторПользователяИБ",
			"ИдентификаторПользователяСервиса");
	КонецЕсли;
	
	Выгрузка = Запрос.Выполнить().Выгрузить();
	
	Если Пользователь = Неопределено И УникальныйИдентификатор = Неопределено Тогда
		Если Выгрузка.Количество() = 0 Тогда
			Возврат;
		КонецЕсли;
	Иначе
		Если Выгрузка.Количество() < 2 Тогда
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	НеоднозначныеИдентификаторы = Выгрузка.ВыгрузитьКолонку("НеоднозначныйИдентификатор");
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("НеоднозначныеИдентификаторы", НеоднозначныеИдентификаторы);
	Запрос.Текст =
	"ВЫБРАТЬ
	|	НеоднозначныеИдентификаторы.НеоднозначныйИдентификатор КАК НеоднозначныйИдентификатор,
	|	НеоднозначныеИдентификаторы.Пользователь КАК Пользователь
	|ИЗ
	|	(ВЫБРАТЬ
	|		Пользователи.ИдентификаторПользователяИБ КАК НеоднозначныйИдентификатор,
	|		Пользователи.Ссылка КАК Пользователь
	|	ИЗ
	|		Справочник.Пользователи КАК Пользователи
	|	ГДЕ
	|		Пользователи.ИдентификаторПользователяИБ В(&НеоднозначныеИдентификаторы)
	|	
	|	ОБЪЕДИНИТЬ ВСЕ
	|	
	|	ВЫБРАТЬ
	|		ВнешниеПользователи.ИдентификаторПользователяИБ,
	|		ВнешниеПользователи.Ссылка
	|	ИЗ
	|		Справочник.ВнешниеПользователи КАК ВнешниеПользователи
	|	ГДЕ
	|		ВнешниеПользователи.ИдентификаторПользователяИБ В(&НеоднозначныеИдентификаторы)) КАК НеоднозначныеИдентификаторы
	|
	|УПОРЯДОЧИТЬ ПО
	|	НеоднозначныеИдентификаторы.НеоднозначныйИдентификатор,
	|	НеоднозначныеИдентификаторы.Пользователь";
	
	Выгрузка = Запрос.Выполнить().Выгрузить();
	
	ОписаниеОшибки = "";
	ТекущийНеоднозначныйИдентификатор = Неопределено;
	
	Для Каждого Строка Из Выгрузка Цикл
		Если Строка.НеоднозначныйИдентификатор <> ТекущийНеоднозначныйИдентификатор Тогда
			ТекущийНеоднозначныйИдентификатор = Строка.НеоднозначныйИдентификатор;
			Если ТипЗнч(НайденныеИдентификаторы) = Тип("Соответствие") Тогда
				ТекущиеПользователи = Новый Массив;
				НайденныеИдентификаторы.Вставить(ТекущийНеоднозначныйИдентификатор, ТекущиеПользователи);
			Иначе
				ТекущийПользовательИБ = ПользователиИнформационнойБазы.ТекущийПользователь();
				
				Если ТекущийПользовательИБ.УникальныйИдентификатор <> ТекущийНеоднозначныйИдентификатор Тогда
					ТекущийПользовательИБ =
						ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(
							ТекущийНеоднозначныйИдентификатор);
				КонецЕсли;
				
				Если ТекущийПользовательИБ = Неопределено Тогда
					ИмяДляВхода = "<" + НСтр("ru = 'не найден'") + ">";
				Иначе
					ИмяДляВхода = ТекущийПользовательИБ.Имя;
				КонецЕсли;
				
				Если ИдентификаторПользователяСервиса Тогда
					ОписаниеОшибки = ОписаниеОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Пользователю сервиса с идентификатором ""%1""
						           |соответствует более одного элемента в справочнике:'"),
						ТекущийНеоднозначныйИдентификатор);
				Иначе
					ОписаниеОшибки = ОписаниеОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Пользователю ИБ ""%1"" с идентификатором ""%2""
						           |соответствует более одного элемента в справочнике:'"),
						ИмяДляВхода,
						ТекущийНеоднозначныйИдентификатор);
				КонецЕсли;
				ОписаниеОшибки = ОписаниеОшибки + Символы.ПС;
			КонецЕсли;
		КонецЕсли;
		
		Если ТипЗнч(НайденныеИдентификаторы) = Тип("Соответствие") Тогда
			ТекущиеПользователи.Добавить(Строка.Пользователь);
		Иначе
			ОписаниеОшибки = ОписаниеОшибки + "- "
				+ СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = '""%1"" %2'"),
					Строка.Пользователь,
					ПолучитьНавигационнуюСсылку(Строка.Пользователь)) + Символы.ПС;
		КонецЕсли;
	КонецЦикла;
	
	Если ТипЗнч(НайденныеИдентификаторы) <> Тип("Соответствие") Тогда
		ВызватьИсключение ОписаниеОшибки;
	КонецЕсли;
	
КонецПроцедуры

// Возвращает сохраняемое значение пароля для указанного пароля.
//
// Параметры:
//  Пароль - Строка - пароль, для которого нужно получить сохраняемое значение.
//
// Возвращаемое значение:
//  Строка - сохраняемое значение пароля.
//
Функция СохраняемоеЗначениеСтрокиПароля(Знач Пароль) Экспорт
	
	Возврат ПользователиСлужебный.СохраняемоеЗначениеСтрокиПароля(Пароль);
	
КонецФункции

// Создает новый пароль, соответствующий заданным правилам проверки сложности.
// Для облегчения запоминания, пароль формируется из слогов (согласная-гласная).
//
// Параметры:
//  СвойстваПароля - см. СвойстваПароля
//                 - Число - устарел (наименьшая длина)
//  УдалитьСложный         - Булево - устарел, следует использовать СвойстваПароля.
//  УдалитьУчестьНастройки - Строка - устарел, следует использовать СвойстваПароля.
//
// Возвращаемое значение:
//  Строка - новый пароль.
//
Функция СоздатьПароль(Знач СвойстваПароля = 7, УдалитьСложный = Ложь, УдалитьУчестьНастройки = "ДляПользователей") Экспорт
	
	Если ТипЗнч(СвойстваПароля) = Тип("Число") Тогда
		НаименьшаяДлина = СвойстваПароля; 
		СвойстваПароля = СвойстваПароля();
		СвойстваПароля.НаименьшаяДлина = НаименьшаяДлина;
		СвойстваПароля.Сложный = УдалитьСложный;
		СвойстваПароля.УчестьНастройки = УдалитьУчестьНастройки;
	КонецЕсли;
	
	Если СвойстваПароля.УчестьНастройки = "ДляВнешнихПользователей"
	 Или СвойстваПароля.УчестьНастройки = "ДляПользователей" Тогда
		
		ИмяПолитикиПаролей = ПользователиСлужебный.ИмяПолитикиПаролей(
			СвойстваПароля.УчестьНастройки = "ДляВнешнихПользователей");
		
		УстановитьПривилегированныйРежим(Истина);
		ПолитикаПаролей = ПолитикиПаролейПользователей.НайтиПоИмени(ИмяПолитикиПаролей);
		Если ПолитикаПаролей = Неопределено Тогда
			МинимальнаяДлинаПароля = ПолучитьМинимальнуюДлинуПаролейПользователей();
			СложныйПароль          = ПолучитьПроверкуСложностиПаролейПользователей();
		Иначе
			МинимальнаяДлинаПароля = ПолитикаПаролей.МинимальнаяДлинаПаролей;
			СложныйПароль          = ПолитикаПаролей.ПроверкаСложностиПаролей;

		КонецЕсли;
		УстановитьПривилегированныйРежим(Ложь);
		Если МинимальнаяДлинаПароля < СвойстваПароля.НаименьшаяДлина Тогда
			МинимальнаяДлинаПароля = СвойстваПароля.НаименьшаяДлина;
		КонецЕсли;
		Если Не СложныйПароль И СвойстваПароля.Сложный Тогда
			СложныйПароль = Истина;
		КонецЕсли;
	Иначе
		МинимальнаяДлинаПароля = СвойстваПароля.НаименьшаяДлина;
		СложныйПароль = СвойстваПароля.Сложный;
	КонецЕсли;
	
	ПараметрыПароля = ПользователиСлужебный.ПараметрыПароля(МинимальнаяДлинаПароля, СложныйПароль);
	
	Возврат ПользователиСлужебный.СоздатьПароль(ПараметрыПароля, СвойстваПароля.ГСЧ);
	
КонецФункции

// Описание свойств пароля, используемых в функции СоздатьПароль.
// 
// Возвращаемое значение:
//   Структура:
//     * НаименьшаяДлина - Число - наименьшая длина пароля.
//     * Сложный - Булево - учитывать требования сложности пароля.
//     * УчестьНастройки - Строка -
//             "НеУчитыватьНастройки" - не учитывать настройки администратора,
//             "ДляПользователей" - учесть настройки для пользователей (по умолчанию),
//             "ДляВнешнихПользователей" - учесть настройки для внешних пользователей.
//             Если настройки администратора учитываются, тогда указанные параметры
//             длины и сложности пароля будут увеличены до значений, указанных в настройках.
//     * ГСЧ - ГенераторСлучайныхЧисел - если уже используется.
//           - Неопределено - создать новый.
//
Функция СвойстваПароля() Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("НаименьшаяДлина", 7);
	Результат.Вставить("Сложный", Ложь);
	Результат.Вставить("УчестьНастройки", "ДляПользователей");
	
	Миллисекунды = ТекущаяУниверсальнаяДатаВМиллисекундах();
	НачальноеЧисло = Миллисекунды - Цел(Миллисекунды / 40) * 40;
	ГСЧ = Новый ГенераторСлучайныхЧисел(НачальноеЧисло);
	
	Результат.Вставить("ГСЧ", ГСЧ);
	
	Возврат Результат;
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Прочие процедуры и функции.

// Определяет, предусмотрено ли в конфигурации использование настроек входа, общих для пользователей:
// сложность пароля, смена пароля, ограничения срока работы в программе и другие.
// См. свойство ОбщиеНастройкиВхода в ПользователиПереопределяемый.ПриОпределенииНастроек.
//
// Возвращаемое значение:
//  Булево - Истина, если в конфигурации предусмотрены общие настройки входа пользователей.
//
Функция ОбщиеНастройкиВходаИспользуются() Экспорт
	
	Возврат ПользователиСлужебныйПовтИсп.Настройки().ОбщиеНастройкиВхода;
	
КонецФункции

// Возвращает назначение ролей, указанное разработчиками библиотек и прикладных решений.
// Область применения: только для автоматизированной проверки конфигурации.
//
// Возвращаемое значение:
//  Структура - смотри одноименный параметр в процедуре ПриОпределенииНазначенияРолей
//              общего модуля ПользователиПереопределяемый.
//
Функция НазначениеРолей() Экспорт
	
	НазначениеРолей = Новый Структура;
	НазначениеРолей.Вставить("ТолькоДляАдминистраторовСистемы",                Новый Массив);
	НазначениеРолей.Вставить("ТолькоДляПользователейСистемы",                  Новый Массив);
	НазначениеРолей.Вставить("ТолькоДляВнешнихПользователей",                  Новый Массив);
	НазначениеРолей.Вставить("СовместноДляПользователейИВнешнихПользователей", Новый Массив);
	
	ПользователиПереопределяемый.ПриОпределенииНазначенияРолей(НазначениеРолей);
	ИнтеграцияПодсистемБСП.ПриОпределенииНазначенияРолей(НазначениеРолей);
	
	Для Каждого Роль Из Метаданные.Роли Цикл
		Расширение = Роль.РасширениеКонфигурации();
		Если Расширение = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		ИмяРоли = Роль.Имя;
		
		Если СтрЗаканчиваетсяНа(ВРег(ИмяРоли), ВРег("ОбщиеПрава")) Тогда
			НазначениеРолей.СовместноДляПользователейИВнешнихПользователей.Добавить(ИмяРоли);
			
		ИначеЕсли СтрЗаканчиваетсяНа(ВРег(ИмяРоли), ВРег("БазовыеПраваВнешнихПользователей")) Тогда
			НазначениеРолей.ТолькоДляВнешнихПользователей.Добавить(ИмяРоли);
			
		ИначеЕсли СтрЗаканчиваетсяНа(ВРег(ИмяРоли), ВРег("АдминистраторСистемы")) Тогда
			НазначениеРолей.ТолькоДляАдминистраторовСистемы.Добавить(ИмяРоли);
		КонецЕсли;
	КонецЦикла;
	
	Возврат НазначениеРолей;
	
КонецФункции

// Проверяет соответствие прав ролей назначению ролей, указанному 
// в процедуре ПриОпределенииНазначенияРолей общего модуля ПользователиПереопределяемый.
//
// Применяется в случаях:
//  - проверка безопасности конфигураций перед автоматическим обновлением на новую версию;
//  - проверка конфигурации перед сборкой;
//  - проверка конфигурации при разработке.
//
// Параметры:
//  ПроверитьВсе - Булево - если Ложь, тогда пропускается проверка назначения ролей
//                          по требованиям технологий сервиса (что быстрее), в противном случае
//                          проверка выполняется, если разделение включено.
//
//  СписокОшибок - Неопределено   - если ошибки найдены, формируется текст ошибок и вызывается исключение.
//               - СписокЗначений - возвращаемое значение. Найденные ошибки добавляются в список без вызова исключения:
//                   * Значение      - Строка - имя роли.
//                                   - Неопределено - роль, указанная в процедуре, не существует в метаданных.
//                   * Представление - Строка - текст ошибки.
//
Процедура ПроверитьНазначениеРолей(ПроверитьВсе = Ложь, СписокОшибок = Неопределено) Экспорт
	
	НазначениеРолей = ПользователиСлужебныйПовтИсп.НазначениеРолей();
	
	ПользователиСлужебный.ПроверитьНазначениеРолей(НазначениеРолей, ПроверитьВсе, СписокОшибок);
	
КонецПроцедуры

// Добавляет всех администраторов системы в группу доступа,
// связанную с предопределенным профилем ОткрытиеВнешнихОтчетовИОбработок.
// Скрывает предупреждения безопасности, всплывающие при первом открытии сеанса администратора.
// Не для модели сервиса.
//
// Параметры:
//   ОткрытиеРазрешено - Булево - если Истина, установить разрешение открытия.
//
Процедура УстановитьПравоОткрытияВнешнихОтчетовИОбработок(ОткрытиеРазрешено) Экспорт
	
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		Возврат;
	КонецЕсли;
	
	ПараметрыАдминистрирования = СтандартныеПодсистемыСервер.ПараметрыАдминистрирования();
	ПараметрыАдминистрирования.Вставить("ПринятоРешениеПоОткрытиюВнешнихОтчетовИОбработок", Истина);
	СтандартныеПодсистемыСервер.УстановитьПараметрыАдминистрирования(ПараметрыАдминистрирования);
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом") Тогда
		МодульУправлениеДоступомСлужебный = ОбщегоНазначения.ОбщийМодуль("УправлениеДоступомСлужебный");
		МодульУправлениеДоступомСлужебный.УстановитьПравоОткрытияВнешнихОтчетовИОбработок(ОткрытиеРазрешено);
		Возврат;
	КонецЕсли;
	
	РольАдминистраторСистемы = Метаданные.Роли.АдминистраторСистемы;
	РольИнтерактивноеОткрытие = Метаданные.Роли.ИнтерактивноеОткрытиеВнешнихОтчетовИОбработок;
	
	ПользователиИБ = ПользователиИнформационнойБазы.ПолучитьПользователей();
	Для Каждого ПользовательИБ Из ПользователиИБ Цикл
		
		Если Не ПользовательИБ.Роли.Содержит(РольАдминистраторСистемы) Тогда
			Продолжить;
		КонецЕсли;
		
		ПользовательИзменен = Ложь;
		ЕстьРольИнтерактивногоОткрытия = ПользовательИБ.Роли.Содержит(РольИнтерактивноеОткрытие);
		Если ОткрытиеРазрешено Тогда 
			Если Не ЕстьРольИнтерактивногоОткрытия Тогда 
				ПользовательИБ.Роли.Добавить(РольИнтерактивноеОткрытие);
				ПользовательИзменен = Истина;
			КонецЕсли;
		Иначе 
			Если ЕстьРольИнтерактивногоОткрытия Тогда
				ПользовательИБ.Роли.Удалить(РольИнтерактивноеОткрытие);
				ПользовательИзменен = Истина;
			КонецЕсли;
		КонецЕсли;
		Если ПользовательИзменен Тогда 
			ПользовательИБ.Записать();
		КонецЕсли;
		
		ОписаниеНастроек = Новый ОписаниеНастроек;
		ОписаниеНастроек.Представление = НСтр("ru = 'Предупреждение безопасности'");
		ОбщегоНазначения.ХранилищеОбщихНастроекСохранить(
			"ПредупреждениеБезопасности", 
			"ПользовательОзнакомлен", 
			Истина, 
			ОписаниеНастроек, 
			ПользовательИБ.Имя);
		
	КонецЦикла;
	
КонецПроцедуры

// Сохраняет общие настройки входа пользователей.
// Допустимо передавать отдельные свойства.
// 
// Параметры:
//  ОбщиеСохраняемыеНастройки - см. Пользователи.НовоеОписаниеОбщихНастроекВхода
//
Процедура УстановитьОбщиеНастройкиВхода(ОбщиеСохраняемыеНастройки) Экспорт
	
	Блокировка = Новый БлокировкаДанных();
	Блокировка.Добавить("Константа.НастройкиВходаПользователей");
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		
		НастройкиВхода = ПользователиСлужебный.НастройкиВхода();
		Настройки = НастройкиВхода.Общие;
		
		Для Каждого СохраняемаяНастройка Из ОбщиеСохраняемыеНастройки Цикл
			Если Не Настройки.Свойство(СохраняемаяНастройка.Ключ)
			 Или ТипЗнч(Настройки[СохраняемаяНастройка.Ключ]) <> ТипЗнч(СохраняемаяНастройка.Значение) Тогда
				Продолжить;
			КонецЕсли;
			Настройки[СохраняемаяНастройка.Ключ] = СохраняемаяНастройка.Значение;
		КонецЦикла;
		
		Константы.НастройкиВходаПользователей.Установить(Новый ХранилищеЗначения(НастройкиВхода));
		
		Если Не ОбщиеСохраняемыеНастройки.Свойство("ОбновитьТолькоКонстанту") Тогда
			ПользователиСлужебный.ОбновитьОбщуюПолитикуПаролей(Настройки);
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
		
КонецПроцедуры

// Возвращает структуру общих настроек входа по умолчанию.
// 
// Возвращаемое значение:
//  Структура:
//   * ОтдельныеНастройкиДляВнешнихПользователей - Булево - если Ложь,
//       тогда для внешних пользователей применяются настройки обычных пользователей.
//   * КоличествоПопытокВводаПароляДоБлокировки - Число - количество попыток
//       ошибочного ввода пароля до срабатывания блокировки.
//   * ДлительностьБлокировкиВводаПароля - Число - количество минут блокировки ввода пароля.
//   * ПоказыватьВСпискеВыбора - Строка - одна из строк
//       "ВключеноДляНовыхПользователей",  "СкрытоИВключеноДляВсехПользователей"
//       "ВыключеноДляНовыхПользователей", "СкрытоИВыключеноДляВсехПользователей".
//
Функция НовоеОписаниеОбщихНастроекВхода() Экспорт
	
	Настройки = Новый Структура;
	Настройки.Вставить("ОтдельныеНастройкиДляВнешнихПользователей", Ложь);
	Настройки.Вставить("КоличествоПопытокВводаПароляДоБлокировки", 3);
	Настройки.Вставить("ДлительностьБлокировкиВводаПароля", 5);
	Настройки.Вставить("ПоказыватьВСпискеВыбора",
		?(ОбщегоНазначения.РазделениеВключено()
		  Или ВнешниеПользователи.ИспользоватьВнешнихПользователей(),
			"СкрытоИВыключеноДляВсехПользователей", "ВключеноДляНовыхПользователей"));
	
	Возврат Настройки;
	
КонецФункции

// Сохраняет настройки входа пользователей.
// Допустимо передавать отдельные свойства.
// 
// Параметры:
//  СохраняемыеНастройки - см. Пользователи.НовоеОписаниеНастроекВхода
//  ДляВнешнихПользователей - Булево - Истина, если записываются настройки внешних пользователей
//
Процедура УстановитьНастройкиВхода(СохраняемыеНастройки, ДляВнешнихПользователей = Ложь) Экспорт
	
	Блокировка = Новый БлокировкаДанных();
	Блокировка.Добавить("Константа.НастройкиВходаПользователей");
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		НастройкиВхода = ПользователиСлужебный.НастройкиВхода();
		
		Если ДляВнешнихПользователей Тогда
			Настройки = НастройкиВхода.ВнешниеПользователи;
		Иначе
			Настройки = НастройкиВхода.Пользователи;
		КонецЕсли;
		
		Для Каждого СохраняемаяНастройка Из СохраняемыеНастройки Цикл
			
			Если Не Настройки.Свойство(СохраняемаяНастройка.Ключ)
			 Или ТипЗнч(Настройки[СохраняемаяНастройка.Ключ]) <> ТипЗнч(СохраняемаяНастройка.Значение)
			 Или ВРег(СохраняемаяНастройка.Ключ) = ВРег("ПросрочкаРаботыВПрограммеДатаВключения")
			   И Не ЗначениеЗаполнено(Настройки[СохраняемаяНастройка.Ключ]) Тогда
				Продолжить;
			КонецЕсли;
			Настройки[СохраняемаяНастройка.Ключ] = СохраняемаяНастройка.Значение;
		КонецЦикла;
		
		Если Не ЗначениеЗаполнено(Настройки.ПросрочкаРаботыВПрограммеДоЗапрещенияВхода) Тогда
			Настройки.ПросрочкаРаботыВПрограммеДатаВключения = Дата(1, 1, 1);
		ИначеЕсли Не ЗначениеЗаполнено(Настройки.ПросрочкаРаботыВПрограммеДатаВключения) Тогда
			Настройки.ПросрочкаРаботыВПрограммеДатаВключения = НачалоДня(ТекущаяДатаСеанса());
		КонецЕсли;
		
		Константы.НастройкиВходаПользователей.Установить(Новый ХранилищеЗначения(НастройкиВхода));
		
		Если Не СохраняемыеНастройки.Свойство("ОбновитьТолькоКонстанту") Тогда
			Если ДляВнешнихПользователей Тогда
				Если НастройкиВхода.Общие.ОтдельныеНастройкиДляВнешнихПользователей
				   И ОбщиеНастройкиВходаИспользуются() Тогда
				
					ПользователиСлужебный.ОбновитьПолитикуПаролейВнешнихПользователей(Настройки);
				Иначе
					ПользователиСлужебный.ОбновитьПолитикуПаролейВнешнихПользователей(Неопределено);
				КонецЕсли;
			Иначе
				ПользователиСлужебный.ОбновитьПолитикуПаролейПользователей(Настройки);
			КонецЕсли;
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Возвращает структуру настроек входа по умолчанию.
// 
// Возвращаемое значение:
//  Структура:
//   * ПарольДолженОтвечатьТребованиямСложности - Булево - должен ли пароль
//        отвечать требованиям сложности
//          длина не менее 7 символов;
//          содержит любые 3 из 4-х типов символов
//            заглавные буквы, строчные буквы, цифры, специальные символы;
//          не совпадает с именем (для входа).
//   * МинимальнаяДлинаПароля - Число - минимальная длина пароля.
//   * МаксимальныйСрокДействияПароля - Число - максимальное количество дней действия пароля.
//   * МинимальныйСрокДействияПароля - Число - минимальное количество дней действия пароля.
//   * ЗапретитьПовторениеПароляСредиПоследних - Число - количество
//        последних паролей, которые запрещено использовать повторно.
//   * ПредупреждатьОбОкончанииСрокаДействияПароля - Число - количество дней
//        до срока окончания действия пароля, начиная с которого нужно начать предупреждать.
//   * ПросрочкаРаботыВПрограммеДоЗапрещенияВхода - Число - количество дней
//        неактивности пользователя, после чего будет запрещен вход.
//   * ПросрочкаРаботыВПрограммеДатаВключения - Дата - служебное, заполняется автоматически
//        в момент записи ненулевого количества дней просрочки работы вместо нулевого.
//
Функция НовоеОписаниеНастроекВхода() Экспорт
	
	Настройки = Новый Структура();
	// Требования сложности.
	Настройки.Вставить("ПарольДолженОтвечатьТребованиямСложности", Ложь);
	Настройки.Вставить("МинимальнаяДлинаПароля", 0);
	// Требования срока действия.
	Настройки.Вставить("МаксимальныйСрокДействияПароля", 0);
	Настройки.Вставить("МинимальныйСрокДействияПароля", 0);
	Настройки.Вставить("ЗапретитьПовторениеПароляСредиПоследних", 0);
	Настройки.Вставить("ПредупреждатьОбОкончанииСрокаДействияПароля", 0);
	// Требование периодической работы в программе.
	Настройки.Вставить("ПросрочкаРаботыВПрограммеДоЗапрещенияВхода", 0);
	Настройки.Вставить("ПросрочкаРаботыВПрограммеДатаВключения", '00010101');
	
	Возврат Настройки;
	
КонецФункции

#Область УстаревшиеПроцедурыИФункции

// Больше не требуется, так как использованные пароли хранятся в пользователе информационной базы и
// добавляются автоматически при изменении пароля или сохраняемого значения пароля.
// 
// Параметры:
//  Пользователь - СправочникСсылка.Пользователи
//               - СправочникСсылка.ВнешниеПользователи
//  СохраняемоеЗначениеПароля - Строка
//
Процедура ДобавитьИспользованныйПароль(Пользователь, СохраняемоеЗначениеПароля) Экспорт
	Возврат;
КонецПроцедуры

#КонецОбласти

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

// Формирует краткое описание ошибки, которое увидит пользователь,
// а также может записать подробное описание ошибки в журнал регистрации.
//
// Параметры:
//  ШаблонОшибки       - Строка - шаблон, содержащий параметр "%1" для представления пользователя ИБ и
//                       параметр %2 для описания ошибки.
//
//  ИмяДляВхода        - Строка - имя пользователя информационной базы, используемое для входа.
//
//  ИдентификаторПользователяИБ - Неопределено
//                              - УникальныйИдентификатор.
//
//  ИнформацияОбОшибке - ИнформацияОбОшибке.
//
//  ЗаписатьВЖурнал    - Булево - если Истина, то выполняется запись подробного описания ошибки
//                       в журнал регистрации.
//
// Возвращаемое значение:
//  Строка - описание ошибки для просмотра пользователю.
//
Функция ОписаниеОшибкиПриЗаписиПользователяИБ(ШаблонОшибки,
                                              ИмяДляВхода,
                                              ИдентификаторПользователяИБ,
                                              ИнформацияОбОшибке = Неопределено,
                                              ЗаписатьВЖурнал = Истина)
	
	Если ЗаписатьВЖурнал Тогда
		ЗаписьЖурналаРегистрации(
			НСтр("ru = 'Пользователи.Ошибка записи пользователя ИБ'",
			     ОбщегоНазначения.КодОсновногоЯзыка()),
			УровеньЖурналаРегистрации.Ошибка,
			,
			,
			СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонОшибки,
				"""" + ИмяДляВхода + """ (" + ?(ЗначениеЗаполнено(ИдентификаторПользователяИБ),
					НСтр("ru = 'Новый'"), Строка(ИдентификаторПользователяИБ)) + ")",
				?(ТипЗнч(ИнформацияОбОшибке) = Тип("ИнформацияОбОшибке"),
					ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке), Строка(ИнформацияОбОшибке))));
	КонецЕсли;
	
	Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонОшибки, """" + ИмяДляВхода + """",
		?(ТипЗнч(ИнформацияОбОшибке) = Тип("ИнформацияОбОшибке"),
			ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке), Строка(ИнформацияОбОшибке)));
	
КонецФункции

// Для функций ЭтоПолноправныйПользователь и РолиДоступны.

// Описание
//
// Параметры:
//  Пользователь - Неопределено
//               - ПользовательИнформационнойБазы
//               - СправочникСсылка.ВнешниеПользователи
//               - СправочникСсылка.Пользователи
// 
// Возвращаемое значение:
//  - Неопределено
//  - ФиксированнаяСтруктура
//  - Структура:
//    * УказанТекущийПользовательИБ - Булево
//    * ПользовательИБ - Неопределено
//                     - ПользовательИнформационнойБазы
//
Функция СвойстваПроверяемогоПользователяИБ(Пользователь) Экспорт
	
	СвойстваТекущегоПользовательИБ = ПользователиСлужебныйПовтИсп.СвойстваТекущегоПользователяИБ();
	ПользовательИБ = Неопределено;
	
	Если ТипЗнч(Пользователь) = Тип("ПользовательИнформационнойБазы") Тогда
		ПользовательИБ = Пользователь;
		
	ИначеЕсли Пользователь = Неопределено Или Пользователь = АвторизованныйПользователь() Тогда
		Возврат СвойстваТекущегоПользовательИБ;
	Иначе
		// Задан не текущий пользователь.
		Если ЗначениеЗаполнено(Пользователь) Тогда
			ИдентификаторПользователяИБ = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Пользователь, "ИдентификаторПользователяИБ");
			Если СвойстваТекущегоПользовательИБ.УникальныйИдентификатор = ИдентификаторПользователяИБ Тогда
				Возврат СвойстваТекущегоПользовательИБ;
			КонецЕсли;
			ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(ИдентификаторПользователяИБ);
		КонецЕсли;
	КонецЕсли;
	
	Если ПользовательИБ = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Если СвойстваТекущегоПользовательИБ.УникальныйИдентификатор = ПользовательИБ.УникальныйИдентификатор Тогда
		Возврат СвойстваТекущегоПользовательИБ;
	КонецЕсли;
	
	Свойства = Новый Структура;
	Свойства.Вставить("УказанТекущийПользовательИБ", Ложь);
	Свойства.Вставить("ПользовательИБ", ПользовательИБ);
	
	Возврат Свойства;
	
КонецФункции

#КонецОбласти
